# -*- coding: utf-8 -*-

import sys
import os
import stat
import socket
import time
import json
import datetime

import json
from PySide2.QtWidgets import (QMainWindow, QTreeWidget, QTreeWidgetItem, QPushButton, 
                             QVBoxLayout, QHBoxLayout, QWidget, QSplitter, QFileDialog,
                             QProgressBar, QTextEdit, QLabel, QGroupBox, QHeaderView,
                             QLineEdit, QDialog, QFormLayout, QMessageBox, QTabWidget, QListWidget, QListWidgetItem, QInputDialog, QStyledItemDelegate, QAbstractItemView, QComboBox, QStyleOptionViewItem)
from PySide2.QtCore import Qt, QThread, Signal, QRect, QObject, QTimer, QCoreApplication, QMetaObject, Slot
from PySide2.QtGui import QIcon, QPixmap, QPainter, QPen, QColor, QPolygon, QBrush
from config.settings import Settings
from utils.transfer_worker import UploadWorker, DownloadWorker
from utils.ssh_client import SSHClient


class ProgressSignalWrapper(QObject):
    """进度信号包装器，确保Nuitka环境下正确传递参数"""
    def __init__(self, parent, thread_index, callback):
        super().__init__(parent)
        self.thread_index = thread_index
        self.callback = callback
    
    def emit(self, value, thread_index=None):
        # 直接调用回调，确保在主线程中执行
        if thread_index is None:
            thread_index = self.thread_index
        self.callback(value, thread_index)


class LogSignalWrapper(QObject):
    """日志信号包装器，确保在Nuitka环境下正确传递参数"""
    def __init__(self, parent, log_editor, connection_name, log_callback, log_with_connection_callback):
        super().__init__(parent)
        self.log_editor = log_editor
        self.connection_name = connection_name
        self.log_callback = log_callback
        self.log_with_connection_callback = log_with_connection_callback
    
    def emit(self, message, *args):
        # 直接调用回调，确保在主线程中执行
        self.log_callback(message, self.log_editor)
        self.log_with_connection_callback(message, self.connection_name)


class FinishedSignalWrapper(QObject):
    """完成信号包装器，确保在Nuitka环境下正确传递参数"""
    def __init__(self, parent, connection_name, callback):
        super().__init__(parent)
        self.connection_name = connection_name
        self.callback = callback
    
    def emit(self, *args):
        # 直接调用回调，确保在主线程中执行
        self.callback(self.connection_name)


# 自定义委托用于绘制复选框
class CheckBoxDelegate(QStyledItemDelegate):
    def paint(self, painter, option, index):
        # 绘制默认的项目背景
        super().paint(painter, option, index)
        
        # 绘制自定义复选框
        if index.column() == 0:  # 只在第一列绘制复选框
            # 使用类型忽略注释来绕过类型检查问题
            checkbox_rect = QRect(option.rect.left() + 4,  # type: ignore
                                option.rect.top() + (option.rect.height() - 16) // 2,  # type: ignore
                                16, 16)
            
            # 获取项目的选中状态
            check_state = index.model().data(index, Qt.ItemDataRole.CheckStateRole)
            
            # 绘制复选框背景
            painter.setRenderHint(QPainter.RenderHint.Antialiasing)
            if check_state == Qt.CheckState.Checked:
                # 绘制选中的复选框
                painter.setBrush(QBrush(QColor(74, 144, 226)))  # 蓝色背景
                painter.setPen(QPen(QColor(74, 144, 226), 1))
            else:
                # 绘制未选中的复选框
                painter.setBrush(QBrush(QColor(255, 255, 255)))  # 白色背景
                painter.setPen(QPen(QColor(204, 204, 204), 1))
            
            painter.drawRoundedRect(checkbox_rect, 3, 3)
            
            # 绘制勾选标记
            if check_state == Qt.CheckState.Checked:
                painter.setPen(QPen(QColor(255, 255, 255), 2, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap))

                # 绘制对勾
                painter.drawLine(checkbox_rect.left() + 3, checkbox_rect.top() + 8,
                               checkbox_rect.left() + 6, checkbox_rect.top() + 11)
                painter.drawLine(checkbox_rect.left() + 6, checkbox_rect.top() + 11,
                               checkbox_rect.left() + 12, checkbox_rect.top() + 5)

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.settings = Settings()
        self.upload_threads = []
        self.download_threads = []
        self.init_ui()
        self.apply_styles()
        # 程序启动时重新加载配置（无提示）
        self._reload_default_config_impl(show_message=False)
        
    def apply_styles(self):
        """应用现代化样式表"""
        style = """
        QMainWindow {
            background-color: #f0f0f0;
            font-family: "Segoe UI", "Microsoft YaHei", sans-serif;
        }
        
        QGroupBox {
            background-color: white;
            border: 1px solid #d0d0d0;
            border-radius: 8px;
            margin-top: 1ex;
            font-weight: bold;
            color: #333333;
        }
        
        QGroupBox::title {
            left: 10px;
        }
        
        QPushButton {
            background-color: #4a90e2;
            border: none;
            color: white;
            padding: 8px 16px;
            border-radius: 6px;
            font-size: 13px;
            font-weight: bold;
        }
        
        QPushButton:hover {
            background-color: #357ae8;
        }
        
        QPushButton:pressed {
            background-color: #2a64c4;
        }
        
        QPushButton:disabled {
            background-color: #cccccc;
            color: #666666;
        }
        
        QPushButton[text="添加分组"] {
            background-color: #50c878;
        }
        
        QPushButton[text="添加分组"]:hover {
            background-color: #3eb489;
        }
        
        QPushButton[text="添加连接"] {
            background-color: #9370db;
        }
        
        QPushButton[text="添加连接"]:hover {
            background-color: #7b68ee;
        }
        
        QPushButton[text="测试连接"] {
            background-color: #ffa500;
        }
        
        QPushButton[text="测试连接"]:hover {
            background-color: #ff8c00;
        }
        
        QPushButton[text="编辑"] {
            background-color: #1e90ff;
        }
        
        QPushButton[text="编辑"]:hover {
            background-color: #00bfff;
        }
        
        QPushButton[text="删除"] {
            background-color: #e74c3c;
        }
        
        QPushButton[text="删除"]:hover {
            background-color: #c0392b;
        }
        
        QPushButton[text="上传"] {
            background-color: #50c878;
        }
        
        QPushButton[text="上传"]:hover {
            background-color: #3eb489;
        }
        
        QPushButton[text="下载"] {
            background-color: #9370db;
        }
        
        QPushButton[text="下载"]:hover {
            background-color: #7b68ee;
        }
        
        QPushButton[text="选择文件"] {
            background-color: #4a90e2;
        }
        
        QPushButton[text="选择文件"]:hover {
            background-color: #357ae8;
        }
        
        QPushButton[text="选择文件夹"] {
            background-color: #4a90e2;
        }
        
        QPushButton[text="选择文件夹"]:hover {
            background-color: #357ae8;
        }
        
        QPushButton[text="浏览"] {
            background-color: #50c878;
        }
        
        QPushButton[text="浏览"]:hover {
            background-color: #3eb489;
        }
        
        QPushButton[text="选择文件或目录"] {
            background-color: #4a90e2;
        }
        
        QPushButton[text="选择文件或目录"]:hover {
            background-color: #357ae8;
        }
        
        QPushButton[text="上传配置"] {
            background-color: #4a90e2;
        }
        
        QPushButton[text="上传配置"]:hover {
            background-color: #357ae8;
        }
        
        QPushButton[text="下载配置"] {
            background-color: #4a90e2;
        }
        
        QPushButton[text="下载配置"]:hover {
            background-color: #357ae8;
        }
        
        QLineEdit {
            border: 1px solid #d0d0d0;
            border-radius: 6px;
            padding: 6px;
            background-color: white;
            font-size: 13px;
        }
        
        QLineEdit:focus {
            border: 2px solid #4a90e2;
        }
        
        QTextEdit {
            border: 1px solid #d0d0d0;
            border-radius: 6px;
            background-color: white;
            font-size: 13px;
        }
        
        QTextEdit:disabled {
            background-color: #f0f0f0;
        }
        
        QProgressBar {
            border: 1px solid #d0d0d0;
            border-radius: 6px;
            background-color: #f0f0f0;
            text-align: center;
        }
        
        QProgressBar::chunk {
            background-color: #4a90e2;
            border-radius: 5px;
        }
        
        QTabWidget::pane {
            border: 1px solid #d0d0d0;
            border-radius: 6px;
            background-color: white;
        }
        
        QTabBar::tab {
            background-color: #e0e0e0;
            border: 1px solid #d0d0d0;
            border-bottom: none;
            border-top-left-radius: 6px;
            border-top-right-radius: 6px;
            padding: 8px 16px;
            margin-right: 2px;
            font-weight: bold;
            color: #333333;
        }
        
        QTabBar::tab:selected {
            background-color: #C3D8F2;  /* 浅紫色背景 */
            border-bottom: 2px solid white;
            margin-bottom: -2px;
        }
        
        }
        
        # QTreeWidget {
        #     border: 1px solid #d0d0d0;
        #     border-radius: 6px;
        #     background-color: white;
        #     alternate-background-color: #f9f9f9;
        # }
        # QTreeWidget::item {
        #     padding: 4px;
        # }
        # QTreeWidget::item:selected {
        #     background-color: #cce5ff;
        #     color: #333333;
        # }
        # QTreeWidget::item:hover {
        #     background-color: #e6f2ff;
        # }
        # /* 删除indicator相关QSS，恢复原生复选框√效果 */
        
        QHeaderView::section {
            background-color: #4a90e2;
            color: white;
            padding: 6px;
            border: none;
            font-weight: bold;
        }
        
        QSplitter::handle {
            background-color: #d0d0d0;
            border: 1px solid #d0d0d0;
        }
        
        QSplitter::handle:horizontal {
            width: 8px;
        }
        
        QSplitter::handle:vertical {
            height: 8px;
        }
        """
        self.setStyleSheet(style)
        
    def init_ui(self):
        self.setWindowTitle('SSH文件传输工具')
        self.setGeometry(100, 100, 1200, 800)
        
        # 设置窗口图标 - 使用资源路径函数
        try:
            # 导入main.py中的资源路径函数
            import sys
            # 获取main.py所在目录
            main_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
            sys.path.insert(0, main_dir)
            from main import get_resource_path
            
            icon_path = get_resource_path('pic.ico')
            if os.path.exists(icon_path):
                self.setWindowIcon(QIcon(icon_path))
        except Exception as e:
            # 如果导入失败，尝试直接使用相对路径
            icon_path = 'pic.ico'
            if os.path.exists(icon_path):
                self.setWindowIcon(QIcon(icon_path))
        
        # 创建主分割器
        main_splitter = QSplitter()
        main_splitter.setOrientation(Qt.Orientation.Horizontal)

        main_splitter.setHandleWidth(8)  # 设置分割器手柄宽度
        main_splitter.setChildrenCollapsible(False)  # 防止子控件被压缩到消失
        
        # 左侧SSH连接管理区域
        self.create_ssh_panel(main_splitter)
        
        # 右侧标签页面板
        self.create_right_panel(main_splitter)
        
        # 添加到主分割器
        main_splitter.addWidget(self.ssh_panel)
        main_splitter.addWidget(self.right_panel)
        main_splitter.setSizes([300, 900])
        
        # 设置中央窗口
        central_widget = QWidget()
        layout = QHBoxLayout(central_widget)
        layout.setContentsMargins(10, 10, 10, 10)  # 设置边距
        layout.setSpacing(10)  # 设置间距
        layout.addWidget(main_splitter)
        self.setCentralWidget(central_widget)
        
        # 初始化SSH连接树
        self.load_ssh_connections()
        
    def create_ssh_panel(self, parent):
        # 创建包含配置区域和SSH连接管理区域的整体面板
        self.ssh_panel = QWidget()
        layout = QVBoxLayout(self.ssh_panel)
        layout.setSpacing(10)
        layout.setContentsMargins(0, 0, 0, 0)
        
        # 创建配置区域
        self.create_config_panel(layout)
        
        # 创建SSH连接管理区域
        self.create_ssh_manager_panel(layout)
        
    def create_config_panel(self, parent_layout):
        """创建配置区域"""
        config_group = QGroupBox("配置区域")
        config_layout = QHBoxLayout(config_group)
        config_layout.setSpacing(8)
        config_layout.setContentsMargins(10, 15, 10, 10)
        
        # 上传配置按钮
        self.upload_config_btn = QPushButton("上传配置")
        self.upload_config_btn.clicked.connect(self.open_upload_config_dialog)
        
        # 下载配置按钮
        self.download_config_btn = QPushButton("下载配置")
        self.download_config_btn.clicked.connect(self.open_download_config_dialog)
        
        # 重新加载配置按钮
        self.reload_config_btn = QPushButton("重新加载")
        self.reload_config_btn.clicked.connect(self.reload_default_config)
        
        config_layout.addWidget(self.upload_config_btn)
        config_layout.addWidget(self.download_config_btn)
        config_layout.addWidget(self.reload_config_btn)
        config_layout.addStretch()  # 添加弹性空间
        
        parent_layout.addWidget(config_group)
        
    def reload_default_config(self):
        """重新加载默认配置"""
        self._reload_default_config_impl(show_message=True)
        
    def _reload_default_config_impl(self, show_message=False):
        """重新加载默认配置的实现"""
        # 重新加载上传和下载的默认配置
        # 重新加载上传配置
        default_upload_dir = self.settings.get_default_upload_dir()
        self.upload_dir_edit.setText(default_upload_dir)
        
        # 重新加载下载配置
        default_download_dir = self.settings.get_default_download_dir()
        self.download_dir_edit.setText(default_download_dir)
        
        # 同时重新加载默认上传和下载文件列表
        self.upload_file_list.clear()
        default_upload_files = self.settings.get_default_upload_files()
        for file in default_upload_files:
            item = QTreeWidgetItem(self.upload_file_list)
            item.setText(0, file)
            # 根据路径判断是文件还是目录，设置相应图标
            if os.path.isfile(file):
                item.setIcon(0, self.style().standardIcon(self.style().StandardPixmap.SP_FileIcon))
            elif os.path.isdir(file):
                item.setIcon(0, self.style().standardIcon(self.style().StandardPixmap.SP_DirIcon))
            else:
                # 对于自定义路径或远程路径，使用信息图标
                item.setIcon(0, self.style().standardIcon(self.style().StandardPixmap.SP_MessageBoxInformation))
        
        self.download_file_list.clear()
        default_download_files = self.settings.get_default_download_files()
        for file in default_download_files:
            item = QTreeWidgetItem(self.download_file_list)
            item.setText(0, file)
            # 对于远程路径，使用信息图标
            item.setIcon(0, self.style().standardIcon(self.style().StandardPixmap.SP_MessageBoxInformation))
        
        if show_message:
            QMessageBox.information(self, "提示", "默认配置已重新加载")
        
    def open_upload_config_dialog(self):
        """打开上传配置对话框"""
        dialog = UploadConfigDialog(self)
        dialog.exec_()
        
    def open_download_config_dialog(self):
        """打开下载配置对话框"""
        dialog = DownloadConfigDialog(self)
        dialog.exec_()
        
    def create_ssh_manager_panel(self, parent_layout):
        """创建SSH连接管理区域"""
        ssh_group = QGroupBox("SSH连接管理")
        layout = QVBoxLayout(ssh_group)
        layout.setSpacing(10)
        layout.setContentsMargins(10, 20, 10, 10)
        
        # 按钮布局（第一行）
        button_layout1 = QHBoxLayout()
        button_layout1.setSpacing(8)
        self.add_group_btn = QPushButton("添加分组")
        self.add_connection_btn = QPushButton("添加连接")
        self.test_connection_btn = QPushButton("测试连接")
        
        # 连接按钮事件
        self.add_group_btn.clicked.connect(self.add_group)
        self.add_connection_btn.clicked.connect(self.add_connection)
        self.test_connection_btn.clicked.connect(self.test_connection)
        
        button_layout1.addWidget(self.add_group_btn)
        button_layout1.addWidget(self.add_connection_btn)
        button_layout1.addWidget(self.test_connection_btn)
        
        # 按钮布局（第二行）
        button_layout2 = QHBoxLayout()
        button_layout2.setSpacing(8)
        self.edit_btn = QPushButton("编辑")
        self.remove_btn = QPushButton("删除")
        self.import_btn = QPushButton("导入连接")
        
        self.edit_btn.clicked.connect(self.edit_item)
        self.remove_btn.clicked.connect(self.remove_item)
        self.import_btn.clicked.connect(self.import_connections)
        
        button_layout2.addWidget(self.edit_btn)
        button_layout2.addWidget(self.remove_btn)
        button_layout2.addWidget(self.import_btn)
        
        # SSH连接树
        self.ssh_tree = QTreeWidget()
        self.ssh_tree.setObjectName("ssh_tree")
        self.ssh_tree.setHeaderLabels(["连接列表"])
        self.ssh_tree.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection)

        self.ssh_tree.setAlternatingRowColors(True)  # 交替行颜色
        self.ssh_tree.setRootIsDecorated(True)  # 显示根装饰
        # 取消选中行的背景高亮，仅保留复选框作为选中效果
        self.ssh_tree.setStyleSheet(
            """
            QTreeWidget {
                selection-background-color: transparent;
            }
            QTreeWidget::item:selected,
            QTreeWidget::item:selected:active,
            QTreeWidget::item:selected:!active {
                background-color: transparent;
                color: inherit;
            }
            """
        )
        
        # 应用自定义委托来绘制复选框（删除自定义委托，使用原生效果）
        # delegate = CheckBoxDelegate()
        # self.ssh_tree.setItemDelegate(delegate)
        
        layout.addLayout(button_layout1)
        layout.addLayout(button_layout2)
        layout.addWidget(self.ssh_tree)
        
        parent_layout.addWidget(ssh_group)
        
    def create_right_panel(self, parent):
        """创建右侧标签页面板"""
        self.right_panel = QGroupBox("功能区域")
        layout = QVBoxLayout(self.right_panel)
        layout.setSpacing(10)
        layout.setContentsMargins(10, 20, 10, 10)
        
        # 创建标签页
        self.tab_widget = QTabWidget()
        self.tab_widget.setMovable(True)  # 允许拖拽标签页
        # 设置标签页的最小宽度，确保标题能完整显示但不过宽
        self.tab_widget.setStyleSheet("""
            QTabBar::tab {
                min-width: 80px;
                padding: 6px 12px;
            }
        """)
        
        # 上传标签页
        self.upload_widget = QWidget()
        self.create_upload_panel(self.upload_widget)
        self.tab_widget.addTab(self.upload_widget, "文件上传")
        
        # 下载标签页
        self.download_widget = QWidget()
        self.create_download_panel(self.download_widget)
        self.tab_widget.addTab(self.download_widget, "文件下载")
        
        layout.addWidget(self.tab_widget)
        
    def create_upload_panel(self, parent):
        """创建上传面板"""
        layout = QVBoxLayout(parent)
        layout.setSpacing(12)
        layout.setContentsMargins(15, 15, 15, 15)
        
        # 上传目录
        upload_dir_layout = QHBoxLayout()
        upload_dir_layout.setSpacing(8)
        self.upload_dir_label = QLabel("上传目录:")
        self.upload_dir_edit = QLineEdit()
        self.upload_dir_edit.setText("/tmp")  # 默认上传目录
        self.browse_remote_dir_btn = QPushButton("浏览")
        self.browse_remote_dir_btn.clicked.connect(self.browse_remote_directory)
        
        upload_dir_layout.addWidget(self.upload_dir_label)
        upload_dir_layout.addWidget(self.upload_dir_edit)
        upload_dir_layout.addWidget(self.browse_remote_dir_btn)
        
        # 上传按钮（移到浏览按钮下一行）
        upload_button_layout = QHBoxLayout()
        upload_button_layout.setSpacing(8)
        upload_file_label = QLabel("选择文件:")  # 添加"选择文件"标签
        self.select_upload_btn = QPushButton("选择文件")
        self.select_upload_dir_btn = QPushButton("选择文件夹")
        
        upload_button_layout.addWidget(upload_file_label)  # 添加标签到布局
        upload_button_layout.addWidget(self.select_upload_btn)
        upload_button_layout.addWidget(self.select_upload_dir_btn)
        
        self.select_upload_btn.clicked.connect(self.select_upload_files)
        self.select_upload_dir_btn.clicked.connect(self.select_upload_dirs)
        
        # 自定义路径
        custom_path_layout = QHBoxLayout()
        custom_path_layout.setSpacing(8)
        custom_path_label = QLabel("自定义路径:")
        self.upload_custom_path_edit = QLineEdit()
        self.add_upload_custom_path_btn = QPushButton("添加")
        
        custom_path_layout.addWidget(custom_path_label)
        custom_path_layout.addWidget(self.upload_custom_path_edit)
        custom_path_layout.addWidget(self.add_upload_custom_path_btn)
        
        self.add_upload_custom_path_btn.clicked.connect(self.add_custom_path_to_upload)
        
        # 上传文件列表（使用QTreeWidget替换QTextEdit）
        self.upload_file_list = QTreeWidget()
        self.upload_file_list.setObjectName("upload_file_list")  # 设置对象名称以便精确应用样式
        self.upload_file_list.setHeaderLabels(["文件/目录"])
        self.upload_file_list.setRootIsDecorated(False)
        self.upload_file_list.setMinimumHeight(100)
        # 仅为上传列表控件设置边框样式，避免影响其他控件（如连接树）
        self.upload_file_list.setStyleSheet("border: 1px solid #d0d0d0; border-radius: 6px;")
        
        # 上传按钮和进度条（移到文件列表下方）
        upload_action_layout = QHBoxLayout()
        upload_action_layout.setSpacing(8)
        self.upload_btn = QPushButton("上传")
        # 添加上传图标
        upload_icon = self.style().standardIcon(self.style().StandardPixmap.SP_ArrowUp)
        self.upload_btn.setIcon(upload_icon)
        self.upload_progress = QProgressBar()
        self.upload_progress.setVisible(False)
        self.upload_progress.setFixedHeight(20)
        
        upload_action_layout.addWidget(self.upload_btn)
        upload_action_layout.addWidget(self.upload_progress)
        
        # 连接上传按钮的点击事件
        self.upload_btn.clicked.connect(self.start_upload)
        
        # 上传日志标签页
        self.upload_log_tabs = QTabWidget()
        # 默认日志页（用于显示总体信息）
        self.upload_log = QTextEdit()
        self.upload_log.setReadOnly(True)
        self.upload_log_tabs.addTab(self.upload_log, "总体日志")
        
        layout.addLayout(upload_dir_layout)
        layout.addLayout(upload_button_layout)
        layout.addLayout(custom_path_layout)  # 添加自定义路径布局
        layout.addWidget(QLabel("待上传文件列表:"))
        layout.addWidget(self.upload_file_list)
        layout.addLayout(upload_action_layout)
        layout.addWidget(QLabel("上传日志:"))
        layout.addWidget(self.upload_log_tabs)
        
    def add_custom_path_to_upload(self):
        """添加自定义路径到待上传文件列表"""
        # 获取用户输入的路径
        custom_path = self.upload_custom_path_edit.text()
        
        # 去除首尾空白字符
        stripped_path = custom_path.strip()
        
        # 检查路径是否为空
        if not stripped_path:
            if custom_path == "":
                self.upload_log.append("请输入有效的自定义路径（路径不能为空）")
            else:
                self.upload_log.append(f"请输入有效的自定义路径（路径不能只包含空格）")
            return
            
        # 检查路径是否有效（简单检查，不验证路径是否真实存在）
        # 这里我们只做基本的非空检查，因为用户可能输入各种类型的路径
        # 包括本地路径、网络路径、相对路径等
        
        # 添加到上传文件列表
        item = QTreeWidgetItem(self.upload_file_list)
        item.setText(0, stripped_path)
        # 设置一个非文件和目录的图标（使用信息图标）
        item.setIcon(0, self.style().standardIcon(self.style().StandardPixmap.SP_MessageBoxInformation))
        # 清空输入框
        self.upload_custom_path_edit.clear()
        # 在日志中记录添加的路径
        self.upload_log.append(f"已添加自定义路径: {stripped_path}")
            
    def select_upload_files(self):
        """选择要上传的文件"""
        files, _ = QFileDialog.getOpenFileNames(self, "选择文件", "", "All Files (*)")
        if files:
            # 清空当前列表
            self.upload_file_list.clear()
            
            # 添加文件到列表
            for file in files:
                # 确保使用Windows路径分隔符
                windows_path = file.replace("/", "\\")
                item = QTreeWidgetItem(self.upload_file_list)
                item.setText(0, windows_path)
                # 设置文件图标
                item.setIcon(0, self.style().standardIcon(self.style().StandardPixmap.SP_FileIcon))
                
    def select_upload_dirs(self):
        """选择要上传的目录"""
        # 使用系统对话框选择单个目录
        directory = QFileDialog.getExistingDirectory(self, "选择目录")
        if directory:
            # 清空当前列表
            self.upload_file_list.clear()
            
            # 将选中的文件夹路径添加到列表中
            # 确保使用Windows路径分隔符
            windows_path = directory.replace("/", "\\")
            item = QTreeWidgetItem(self.upload_file_list)
            item.setText(0, windows_path)
            # 设置目录图标
            item.setIcon(0, self.style().standardIcon(self.style().StandardPixmap.SP_DirIcon))
    # 注意：以下start_upload方法已被删除，使用后面定义的多线程版本
    # def start_upload(self):
    #     """开始上传（串行执行）"""
    #     ...
    
    def _execute_serial_upload(self, files, connections, upload_dir, index):
        """串行执行上传任务"""
        if index >= len(connections):
            # 所有连接都已完成上传
            self.upload_progress.setVisible(False)
            self.upload_btn.setEnabled(True)
            self.upload_log.append("=" * 50)
            self.upload_log.append("所有服务器上传完成！")
            self.show_message_box("完成", f"已完成向 {len(connections)} 台服务器的文件上传")
            return
            
        # 获取当前连接
        connection = connections[index]
        connection_name = f"{connection['name']} ({connection['host']})"
        
        # 为当前连接创建日志标签页
        log_editor = QTextEdit()
        log_editor.setReadOnly(True)
        self.upload_log_tabs.addTab(log_editor, connection_name)
        self.upload_log_editors[connection_name] = log_editor
        
        # 创建连接的深拷贝
        import copy
        connection_copy = copy.deepcopy(connection)
        
        try:
            # 直接执行上传任务（不使用线程）
            worker = UploadWorker(files, connection_copy, upload_dir)
            
            # 连接信号
            from functools import partial
            worker.log.connect(partial(self._append_upload_log, log_editor=log_editor))
            worker.log.connect(partial(self._append_upload_log_with_connection, connection_name=connection_name))
            worker.progress.connect(partial(self._update_serial_upload_progress, index=index))
            
            # 连接完成信号到下一个任务 - 使用partial避免闭包问题
            from functools import partial
            worker.finished.connect(partial(self._execute_serial_upload, files, connections, upload_dir, index + 1))
            
            # 执行上传
            self.upload_log.append(f"开始上传到: {connection_name}")
            worker.run()
            
        except Exception as e:
            self.upload_log.append(f"上传到 {connection_name} 失败: {str(e)}")
            # 继续执行下一个连接
            self._execute_serial_upload(files, connections, upload_dir, index + 1)
    
    def _update_serial_upload_progress(self, value, index):
        """更新串行上传进度"""
        # 每个连接占用100个进度单位，根据连接索引计算基础进度
        base_progress = index * 100
        current_progress = base_progress + value
        
        # 更新进度条
        if hasattr(self, 'upload_progress'):
            self.upload_progress.setValue(current_progress)
        
    def _append_upload_log(self, message, log_editor):
        """向特定连接的日志编辑器添加日志"""
        log_editor.append(message)
        
    def _append_upload_log_with_connection(self, message, connection_name):
        """向总体日志添加带连接标识的日志"""
        self.upload_log.append(f"[{connection_name}] {message}")
            
    def select_remote_files(self):
        """选择远程文件或目录"""
        # 获取第一个选中的连接
        selected_connections = self.get_checked_connections()
        if not selected_connections:
            self.show_message_box("警告", "请先选择一个SSH连接")
            return
            
        connection = selected_connections[0]
        
        try:
            # 连接到第一个选中的SSH服务器
            ssh_client = SSHClient(
                host=connection["host"],
                port=connection["port"],
                username=connection["username"],
                password=connection["password"]
            )
            ssh_client.connect()
            
            # 获取当前目录（默认为根目录）
            remote_dir = "/"
            
            # 显示目录选择对话框
            dir_dialog = DirectorySelectorDialog(ssh_client, remote_dir, self)
            if dir_dialog.exec_() == QDialog.DialogCode.Accepted:
                selected_items = dir_dialog.get_selected_items()
                if selected_items:
                    # 清空当前列表
                    self.download_file_list.clear()
                    
                    # 显示下载目录（确保使用Windows路径分隔符）
                    download_dir = self.download_dir_edit.text()
                    if not download_dir:
                        download_dir = "."
                    # 确保下载目录使用Windows路径分隔符
                    windows_download_dir = download_dir.replace("/", "\\")
                    # 添加下载目录信息（使用特殊处理，不显示图标）
                    dir_item = QTreeWidgetItem(self.download_file_list)
                    dir_item.setText(0, f"下载目录: {windows_download_dir}")
                    dir_item.setFlags(dir_item.flags() & ~Qt.ItemFlag.ItemIsSelectable)  # 不可选择
                    
                    # 只显示选中的目录或文件，不递归展开
                    # 修改这里：保存选中的项目类型信息，以便下载时判断是否需要递归
                    for item in selected_items:
                        # 获取项目属性以判断是文件还是目录
                        try:
                            stat = ssh_client.sftp.stat(item)
                            tree_item = QTreeWidgetItem(self.download_file_list)
                            if stat.st_mode & 0o040000:  # S_IFDIR - 目录
                                # 对于目录，只显示目录本身，不展开子文件
                                tree_item.setText(0, item)
                                # 设置目录图标
                                tree_item.setIcon(0, self.style().standardIcon(self.style().StandardPixmap.SP_DirIcon))
                            else:  # 文件
                                tree_item.setText(0, item)
                                # 设置文件图标
                                tree_item.setIcon(0, self.style().standardIcon(self.style().StandardPixmap.SP_FileIcon))
                        except Exception:
                            # 如果无法获取属性，按原样显示
                            tree_item = QTreeWidgetItem(self.download_file_list)
                            tree_item.setText(0, item)
                            # 默认设置为文件图标
                            tree_item.setIcon(0, self.style().standardIcon(self.style().StandardPixmap.SP_FileIcon))
                    
            ssh_client.close()
        except Exception as e:
            self.show_message_box("错误", f"连接远程服务器失败: {str(e)}")
    def _get_remote_files_recursive(self, ssh_client, remote_path, file_list):
        """递归获取远程目录中的所有文件"""
        try:
            # 获取项目属性
            stat = ssh_client.sftp.stat(remote_path)
            
            # 检查是否为目录
            if stat.st_mode & 0o040000:  # S_IFDIR - 目录
                # 获取目录内容
                items = ssh_client.sftp.listdir_attr(remote_path)
                
                # 递归处理每个项目
                for item in items:
                    if remote_path == "/":
                        item_path = f"/{item.filename}"
                    else:
                        item_path = f"{remote_path}/{item.filename}"
                    
                    # 检查是否为目录
                    if item.st_mode & 0o040000:  # S_IFDIR - 目录
                        # 递归处理子目录
                        self._get_remote_files_recursive(ssh_client, item_path, file_list)
                    else:  # 文件
                        # 添加文件到列表
                        file_list.append(item_path)
            else:  # 文件
                # 直接添加文件到列表
                file_list.append(remote_path)
        except Exception as e:
            # 如果出现错误，跳过这个项目
            print(f"获取远程文件信息时出错 {remote_path}: {str(e)}")

    def create_download_panel(self, parent):
        """创建下载面板"""
        layout = QVBoxLayout(parent)
        layout.setSpacing(12)
        layout.setContentsMargins(15, 15, 15, 15)
        
        # 下载目录选择
        download_dir_layout = QHBoxLayout()
        download_dir_layout.setSpacing(8)
        self.download_dir_label = QLabel("下载目录:")
        self.download_dir_edit = QLineEdit()
        self.download_dir_edit.setText("download")  # 默认下载目录
        self.browse_local_dir_btn = QPushButton("浏览")
        self.browse_local_dir_btn.clicked.connect(self.browse_local_directory)
        
        download_dir_layout.addWidget(self.download_dir_label)
        download_dir_layout.addWidget(self.download_dir_edit)
        download_dir_layout.addWidget(self.browse_local_dir_btn)
        
        # 远程目录选择
        remote_select_layout = QHBoxLayout()
        remote_select_layout.setSpacing(8)
        select_file_label = QLabel("选择文件:")  # 添加"选择文件"标签
        self.select_remote_btn = QPushButton("选择文件或目录")
        
        remote_select_layout.addWidget(select_file_label)  # 添加标签到布局
        remote_select_layout.addWidget(self.select_remote_btn)
        
        self.select_remote_btn.clicked.connect(self.select_remote_files)
        
        # 自定义下载路径
        custom_path_layout = QHBoxLayout()
        custom_path_layout.setSpacing(8)
        custom_path_label = QLabel("自定义路径:")
        self.download_custom_path_edit = QLineEdit()
        self.add_download_custom_path_btn = QPushButton("添加")
        
        custom_path_layout.addWidget(custom_path_label)
        custom_path_layout.addWidget(self.download_custom_path_edit)
        custom_path_layout.addWidget(self.add_download_custom_path_btn)
        
        self.add_download_custom_path_btn.clicked.connect(self.add_custom_path)
        
        # 下载文件列表（使用QTreeWidget替换QTextEdit）
        self.download_file_list = QTreeWidget()
        self.download_file_list.setObjectName("download_file_list")  # 设置对象名称以便精确应用样式
        self.download_file_list.setHeaderLabels(["文件/目录"])
        self.download_file_list.setRootIsDecorated(False)
        self.download_file_list.setMinimumHeight(100)
        # 仅为下载列表控件设置边框样式，避免影响其他控件（如连接树）
        self.download_file_list.setStyleSheet("border: 1px solid #d0d0d0; border-radius: 6px;")
        
        # 下载按钮和进度条（移到文件列表下方）
        download_action_layout = QHBoxLayout()
        download_action_layout.setSpacing(8)
        self.download_btn = QPushButton("下载")
        # 添加下载图标
        download_icon = self.style().standardIcon(self.style().StandardPixmap.SP_ArrowDown)
        self.download_btn.setIcon(download_icon)
        self.download_progress = QProgressBar()
        self.download_progress.setVisible(False)
        self.download_progress.setFixedHeight(20)
        
        download_action_layout.addWidget(self.download_btn)
        download_action_layout.addWidget(self.download_progress)
        
        # 连接下载按钮的点击事件
        self.download_btn.clicked.connect(self.start_download)
        
        # 下载日志标签页
        self.download_log_tabs = QTabWidget()
        # 默认日志页（用于显示总体信息）
        self.download_log = QTextEdit()
        self.download_log.setReadOnly(True)
        self.download_log_tabs.addTab(self.download_log, "总体日志")
        
        layout.addLayout(download_dir_layout)
        layout.addLayout(remote_select_layout)
        layout.addLayout(custom_path_layout)  # 添加自定义路径布局
        layout.addWidget(QLabel("待下载文件列表:"))
        layout.addWidget(self.download_file_list)
        layout.addLayout(download_action_layout)
        layout.addWidget(QLabel("下载日志:"))
        layout.addWidget(self.download_log_tabs)
        
    def add_custom_path(self):
        """添加自定义路径到待下载文件列表"""
        custom_path = self.download_custom_path_edit.text().strip().strip('"').strip("'")
        if custom_path:
            # 添加到下载文件列表
            item = QTreeWidgetItem(self.download_file_list)
            item.setText(0, custom_path)
            # 设置一个非文件和目录的图标（使用信息图标）
            item.setIcon(0, self.style().standardIcon(self.style().StandardPixmap.SP_MessageBoxInformation))
            # 清空输入框
            self.download_custom_path_edit.clear()
            
    def load_ssh_connections(self):
        """加载SSH连接到树形控件"""
        self.ssh_tree.clear()
        
        # 从配置文件加载分组和连接
        groups = self.settings.get_groups()
        connections = self.settings.get_connections()
        connection_map = {c["id"]: c for c in connections}
        
        if not groups:
            # 如果没有分组，创建一个默认分组
            self.settings.add_group("默认分组")
            groups = self.settings.get_groups()
        
        # 按分组名排序
        groups = sorted(groups, key=lambda x: x["name"])
        
        for group in groups:
            group_item = QTreeWidgetItem(self.ssh_tree, [group["name"]])
            group_item.setFlags(group_item.flags() | Qt.ItemFlag.ItemIsAutoTristate | Qt.ItemFlag.ItemIsUserCheckable)
            group_item.setCheckState(0, Qt.CheckState.Unchecked)
            group_item.setData(0, Qt.ItemDataRole.UserRole, {"type": "group", "id": group["id"]})
            
            # 获取该分组下的连接并按连接名排序
            group_connections = []
            for conn_id in group["connections"]:
                if conn_id in connection_map:
                    group_connections.append(connection_map[conn_id])
            
            # 按连接名排序
            group_connections = sorted(group_connections, key=lambda x: x["name"])
            
            # 添加排序后的连接
            for conn in group_connections:
                conn_item = QTreeWidgetItem(group_item, [f"{conn['name']} ({conn['host']}:{conn['port']})"])
                conn_item.setFlags(conn_item.flags() | Qt.ItemFlag.ItemIsUserCheckable)
                conn_item.setCheckState(0, Qt.CheckState.Unchecked)
                conn_item.setData(0, Qt.ItemDataRole.UserRole, {"type": "connection", "data": conn})
        
        self.ssh_tree.expandAll()
        
    def add_group(self):
        """添加分组"""
        dialog = self.GroupDialog(self)
        if dialog.exec_() == QDialog.DialogCode.Accepted:
            group_name = dialog.get_name()
            if group_name:
                self.settings.add_group(group_name)
                self.load_ssh_connections()
            
    def add_connection(self):
        """添加连接"""
        dialog = ConnectionDialog(self)
        if dialog.exec_() == QDialog.DialogCode.Accepted:
            data = dialog.get_data()
            # 使用用户选择的分组
            group_id = data.get("group_id")
            
            # 如果没有选择分组，则添加到第一个分组或创建新分组
            if group_id is None:
                groups = self.settings.get_groups()
                if groups:
                    group_id = groups[0]["id"]
                else:
                    group = self.settings.add_group("默认分组")
                    group_id = group["id"]
                
            self.settings.add_connection(
                group_id=group_id,
                name=data["name"],
                host=data["host"],
                port=int(data["port"]),
                username=data["username"],
                password=data["password"]
            )
            self.load_ssh_connections()
            
    def test_connection(self):
        """测试连接"""
        selected_connections = self.get_checked_connections()
        if not selected_connections:
            self.show_message_box("警告", "请先勾选一个SSH连接")
            return

        # 创建测试结果对话框
        result_dialog = QDialog(self)
        result_dialog.setWindowTitle("连接测试结果")
        result_dialog.resize(600, 400)
        
        layout = QVBoxLayout()
        result_text = QTextEdit()
        result_text.setReadOnly(True)
        layout.addWidget(result_text)
        
        close_btn = QPushButton("关闭")
        close_btn.clicked.connect(result_dialog.accept)
        layout.addWidget(close_btn)
        
        result_dialog.setLayout(layout)
        
        # 显示测试结果对话框
        result_dialog.show()
        
        # 存储测试线程
        test_threads = []
        
        # 定义结果处理函数
        def handle_test_result(connection, success, message):
            connection_name = f"{connection['name']} ({connection['host']}:{connection['port']})"
            status = "成功" if success else "失败"
            result_text.append(f"{connection_name}: {status} - {message}")
        
        # 定义线程完成处理函数
        def on_thread_finished():
            # 检查是否所有线程都已完成
            # 修复：添加更完善的异常处理，防止访问已删除的线程对象
            try:
                # 过滤掉已经删除的线程对象
                active_threads = []
                for thread, _ in test_threads:
                    try:
                        if thread is not None and thread.isRunning():
                            active_threads.append(thread)
                    except RuntimeError:
                        # 线程对象已被删除，跳过
                        pass
                
                # 如果没有活跃的线程，说明所有测试都已完成
                if not active_threads:
                    result_text.append("\n所有连接测试完成")
            except Exception:
                # 忽略所有异常，确保不会影响程序运行
                pass
        
        # 逐个测试连接
        for connection in selected_connections:
            try:
                # 创建线程和工作器
                thread = QThread()
                worker = ConnectionTestWorker(connection)
                worker.moveToThread(thread)
                
                # 连接信号
                thread.started.connect(worker.run)
                worker.result.connect(handle_test_result)
                worker.finished.connect(thread.quit)
                worker.finished.connect(worker.deleteLater)
                thread.finished.connect(thread.deleteLater)
                thread.finished.connect(on_thread_finished)
                
                # 存储线程引用
                test_threads.append((thread, worker))
                
                # 启动线程
                thread.start()
            except Exception as e:
                connection_name = f"{connection['name']} ({connection['host']}:{connection['port']})"
                result_text.append(f"{connection_name}: 失败 - 创建测试线程时出错: {str(e)}")
        
        # 当对话框关闭时，确保清理所有线程
        def cleanup_on_close():
            for thread, worker in test_threads:
                try:
                    # 修复：添加异常处理，防止访问已删除的线程对象
                    if thread is not None:
                        try:
                            if thread.isRunning():
                                thread.quit()
                                thread.wait(1000)  # 等待最多1秒
                                try:
                                    if thread.isRunning():
                                        thread.terminate()
                                        thread.wait(500)
                                except RuntimeError:
                                    # 线程已被删除
                                    pass
                        except RuntimeError:
                            # 线程已被删除
                            pass
                except RuntimeError:
                    # 线程已被删除
                    pass
        
        result_dialog.finished.connect(cleanup_on_close)
            
    def edit_item(self):
        """编辑选中项"""
        selected_connections = self.get_checked_connections()
        
        # 获取所有勾选的项目（包括分组）
        selected_items = []
        root = self.ssh_tree.invisibleRootItem()
        for i in range(root.childCount()):
            item = root.child(i)
            self._collect_checked_items(item, selected_items)
        
        if not selected_items:
            return
            
        # 如果勾选了多个连接，提示只能编辑一个
        connection_count = sum(1 for item in selected_items 
                             if item.data(0, Qt.ItemDataRole.UserRole).get("type") == "connection")
        
        if connection_count > 1:
            self.show_message_box("警告", "只能编辑1个连接")
            return
            
        # 如果只有一个连接被勾选，编辑该连接
        if connection_count == 1:
            for item in selected_items:
                data = item.data(0, Qt.ItemDataRole.UserRole)
                if data["type"] == "connection":
                    dialog = ConnectionDialog(self, data["data"])
                    if dialog.exec_() == QDialog.DialogCode.Accepted:
                        # 更新连接信息
                        new_data = dialog.get_data()
                        self.settings.update_connection(
                            connection_id=data["data"]["id"],
                            name=new_data["name"],
                            host=new_data["host"],
                            port=int(new_data["port"]),
                            username=new_data["username"],
                            password=new_data["password"]
                        )
                        self.load_ssh_connections()
                    break
        # 如果没有连接被勾选，但有一个分组被勾选，编辑该分组
        elif len(selected_items) == 1:
            item = selected_items[0]
            data = item.data(0, Qt.ItemDataRole.UserRole)
            if data["type"] == "group":
                dialog = self.GroupDialog(self, default_name=item.text(0), title="编辑分组")
                if dialog.exec_() == QDialog.DialogCode.Accepted:
                    group_name = dialog.get_name()
                    if group_name:
                        # 更新分组名称
                        groups = self.settings.get_groups()
                        for group in groups:
                            if group["id"] == data["id"]:
                                self.settings.update_group(data["id"], group_name)
                                break
                        self.load_ssh_connections()
    
    def _collect_checked_items(self, item, selected_items):
        """递归收集选中的项目"""
        if item.checkState(0) == Qt.CheckState.Checked:
            selected_items.append(item)
        elif item.checkState(0) == Qt.CheckState.PartiallyChecked:
            # 检查子项
            for i in range(item.childCount()):
                self._collect_checked_items(item.child(i), selected_items)
                
    def remove_item(self):
        """删除选中项"""
        # 获取所有勾选的项目（包括分组）
        selected_items = []
        root = self.ssh_tree.invisibleRootItem()
        for i in range(root.childCount()):
            item = root.child(i)
            self._collect_checked_items(item, selected_items)
        
        if not selected_items:
            return
            
        # 统计要删除的连接和分组数量
        connections_to_remove = []
        groups_to_remove = []
        
        for item in selected_items:
            data = item.data(0, Qt.ItemDataRole.UserRole)
            if data["type"] == "connection":
                connections_to_remove.append((item, data))
            elif data["type"] == "group":
                groups_to_remove.append((item, data))
        
        # 确认删除
        message = ""
        if connections_to_remove and groups_to_remove:
            message = f"确定要删除 {len(connections_to_remove)} 个连接和 {len(groups_to_remove)} 个分组吗？\n注意：删除分组将同时删除分组内的所有连接。"
        elif connections_to_remove:
            message = f"确定要删除选中的 {len(connections_to_remove)} 个连接吗？"
        elif groups_to_remove:
            message = f"确定要删除选中的 {len(groups_to_remove)} 个分组吗？\n注意：删除分组将同时删除分组内的所有连接。"
        
        if self.confirm_box("确认删除", message):
            # 先删除分组（因为删除分组会同时删除分组内的连接）
            for _, group_data in groups_to_remove:
                self.settings.remove_group(group_data["id"])
            
            # 再删除独立的连接
            for _, connection_data in connections_to_remove:
                self.settings.remove_connection(connection_data["data"]["id"])
            
            # 重新加载连接列表
            self.load_ssh_connections()

    def show_message_box(self, title, text, icon=QMessageBox.Icon.Information):
        """显示中文按钮的消息框"""
        box = QMessageBox(self)
        box.setWindowTitle(title)
        box.setText(text)
        box.setIcon(icon)
        box.addButton("确定", QMessageBox.ButtonRole.AcceptRole)
        box.exec_()

    def confirm_box(self, title, text):
        """显示中文按钮的确认框，返回是否确认"""
        box = QMessageBox(self)
        box.setWindowTitle(title)
        box.setText(text)
        box.setIcon(QMessageBox.Icon.Question)
        yes_btn = box.addButton("是", QMessageBox.ButtonRole.YesRole)
        no_btn = box.addButton("否", QMessageBox.ButtonRole.NoRole)
        box.exec_()
        return box.clickedButton() == yes_btn

    def import_connections(self):
        """从Excel导入连接信息"""
        file_path, _ = QFileDialog.getOpenFileName(self, "选择Excel文件", "", "Excel 文件 (*.xlsx)")
        if not file_path:
            return
        try:
            from openpyxl import load_workbook
        except Exception:
            self.show_message_box("错误", "缺少openpyxl库，请先安装：pip install openpyxl", QMessageBox.Icon.Critical)
            return

        try:
            wb = load_workbook(file_path, read_only=True, data_only=True)
            ws = wb.active
            # 读取表头
            header_row = next(ws.iter_rows(min_row=1, max_row=1, values_only=True))
            headers = [str(h).strip() if h is not None else "" for h in header_row]

            required_fields = ["服务器名", "服务器IP", "登录账号", "登录密码"]
            optional_fields = {"端口": None, "分组名": None}

            # 构建索引映射
            indices = {}
            for field in required_fields:
                if field in headers:
                    indices[field] = headers.index(field)
                else:
                    self.show_message_box("错误", f"Excel缺少必要字段：{field}", QMessageBox.Icon.Critical)
                    return
            for field in optional_fields.keys():
                indices[field] = headers.index(field) if field in headers else None

            imported_count = 0
            # 行遍历
            for i, row in enumerate(ws.iter_rows(min_row=2, values_only=True), start=2):
                def val(idx):
                    return (str(row[idx]).strip() if row[idx] is not None else "") if idx is not None else ""

                name = val(indices["服务器名"]) or ""
                host = val(indices["服务器IP"]) or ""
                username = val(indices["登录账号"]) or ""
                password = val(indices["登录密码"]) or ""
                port_text = val(indices["端口"]) or "22"
                group_name = val(indices["分组名"]) or "默认分组"

                # 校验必要字段
                if not (name and host and username and password):
                    self.show_message_box("错误", f"第{i}行缺少必要字段，导入已终止", QMessageBox.Icon.Critical)
                    return

                # 端口处理
                try:
                    port = int(port_text)
                except Exception:
                    port = 22

                # 查找或创建分组
                groups = self.settings.get_groups()
                target_group = None
                for g in groups:
                    if g["name"] == group_name:
                        target_group = g
                        break
                if target_group is None:
                    target_group = self.settings.add_group(group_name)

                # 添加连接
                self.settings.add_connection(
                    group_id=target_group["id"],
                    name=name,
                    host=host,
                    port=port,
                    username=username,
                    password=password,
                    key_file=None
                )
                imported_count += 1

            self.load_ssh_connections()
            self.show_message_box("导入完成", f"成功导入 {imported_count} 条连接")
        except Exception as e:
            self.show_message_box("错误", f"导入失败：{str(e)}", QMessageBox.Icon.Critical)

    def import_connections_from_excel(self):
        """从Excel导入连接信息（兼容方法）"""
        self.import_connections()

    class GroupDialog(QDialog):
        def __init__(self, parent=None, default_name="", title="添加分组"):
            super().__init__(parent)
            self.setWindowTitle(title)
            self.resize(360, 120)
            # 删除 "?" 按钮
            self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
            
            layout = QFormLayout()
            layout.setContentsMargins(20, 20, 20, 20)
            layout.setHorizontalSpacing(15)
            layout.setVerticalSpacing(12)

            self.name_edit = QLineEdit()
            if default_name:
                self.name_edit.setText(default_name)
            layout.addRow("分组名称:", self.name_edit)

            buttons = QHBoxLayout()
            buttons.addStretch()
            ok_btn = QPushButton("确定")
            cancel_btn = QPushButton("取消")
            ok_btn.clicked.connect(self.accept)
            cancel_btn.clicked.connect(self.reject)
            buttons.addWidget(ok_btn)
            buttons.addWidget(cancel_btn)
            # 增加按钮间距
            buttons.setSpacing(15)
            layout.addRow(buttons)

            self.setLayout(layout)

        def get_name(self):
            return self.name_edit.text().strip()
            
        def start_upload(self, files):
            """开始上传文件"""
            # 获取选中的连接
            selected_connections = self.parent().get_checked_connections()
            if not selected_connections:
                QMessageBox.warning(self, "警告", "请先选择一个SSH连接")
                return
                
            # 获取上传目录
            upload_dir = self.parent().upload_dir_edit.text()
            if not upload_dir:
                QMessageBox.warning(self, "警告", "请先选择上传目录")
                return
                
            try:
                # 为每个选中的连接创建上传线程
                for connection in selected_connections:
                    # 创建连接的深拷贝，避免引用问题
                    import copy
                    connection_copy = copy.deepcopy(connection)
                    
                    # 创建上传工作器
                    upload_worker = UploadWorker(
                        files=files[:],  # 创建文件列表副本
                        connection=connection_copy,
                        upload_dir=upload_dir
                    )
                    
                    # 创建线程
                    thread = QThread()
                    
                    # 将工作器移动到线程中
                    upload_worker.moveToThread(thread)
                    
                    # 连接信号
                    thread.started.connect(upload_worker.run)
                    upload_worker.progress.connect(self.parent().update_upload_progress)
                    upload_worker.finished.connect(self.parent().on_upload_finished)
                    upload_worker.finished.connect(thread.quit)
                    upload_worker.finished.connect(upload_worker.deleteLater)
                    thread.finished.connect(thread.deleteLater)
                    
                    # 启动线程
                    thread.start()
                    
                    # 记录线程
                    if not hasattr(self.parent(), 'upload_threads'):
                        self.parent().upload_threads = []
                    # 使用元组方式存储线程信息
                    connection_name = f"{connection['name']} ({connection['host']}:{connection['port']})"
                    self.parent().upload_threads.append((thread, upload_worker, connection_name))
                        
            except Exception as e:
                QMessageBox.critical(self, "错误", f"上传失败: {str(e)}")
            
    def start_upload(self):
        """开始上传（多线程并发执行）"""
        # 从QTreeWidget获取文件路径
        files = []
        for i in range(self.upload_file_list.topLevelItemCount()):
            item = self.upload_file_list.topLevelItem(i)
            file_path = item.text(0)
            if file_path.strip() and not file_path.startswith("下载目录:"):
                files.append(file_path)
        
        if not files:
            self.upload_log.append("请选择要上传的文件或目录")
            return
            
        # 获取选中的连接
        selected_connections = self.get_checked_connections()
        if not selected_connections:
            self.upload_log.append("请至少选择一个SSH连接")
            return
            
        # 获取上传目录
        upload_dir = self.upload_dir_edit.text()
        if not upload_dir:
            upload_dir = "/tmp"
            
        self.upload_btn.setEnabled(False)
        self.upload_progress.setVisible(True)
        self.upload_progress.setValue(0)
        self.upload_progress.setMaximum(len(selected_connections) * 100)  # 设置最大值为连接数*100
        
        # 清空之前的连接日志标签页（保留总体日志）
        for i in range(self.upload_log_tabs.count() - 1, 0, -1):
            self.upload_log_tabs.removeTab(i)
            
        # 初始化线程管理
        self.upload_threads = []
        self.upload_log_editors = {}  # 存储每个连接的日志编辑器
        self.completed_uploads = 0  # 跟踪完成的上传数量
        
        # 记录开始上传的总体信息
        self.upload_log.append(f"开始向 {len(selected_connections)} 台服务器上传文件...")
        self.upload_log.append(f"上传目录: {upload_dir}")
        self.upload_log.append(f"文件数量: {len(files)}")
        self.upload_log.append("-" * 50)
        
        # 为每个连接创建上传线程和日志标签页
        for i, connection in enumerate(selected_connections):
            # 为每个连接创建日志标签页
            log_editor = QTextEdit()
            log_editor.setReadOnly(True)
            connection_name = f"{connection['name']} ({connection['host']})"
            tab_index = self.upload_log_tabs.addTab(log_editor, connection_name)
            self.upload_log_editors[connection_name] = log_editor
            
            # 创建连接的深拷贝，避免引用问题
            import copy
            connection_copy = copy.deepcopy(connection)
            connection_copy['thread_index'] = i  # 添加线程索引，确保每个连接有唯一标识
            connection_copy['connection_id'] = f"upload_{i}_{int(time.time() * 1000)}"  # 添加唯一ID
            
            # 创建线程和工作器
            thread = QThread()
            worker = UploadWorker(files[:], connection_copy, upload_dir, thread_index=i)  # 创建文件列表副本
            worker.moveToThread(thread)
            
            # 连接信号 - 使用字符串标识符替代闭包，确保Nuitka兼容性
            thread.started.connect(worker.run)
            
            # 使用自定义信号包装器，确保每个连接的信号正确路由
            progress_wrapper = ProgressSignalWrapper(self, i, self.update_upload_progress_for_connection)
            worker.progress.connect(progress_wrapper.emit)
            
            # 使用自定义信号包装器处理日志
            log_wrapper = LogSignalWrapper(self, log_editor, connection_name, 
                                          self._append_upload_log, self._append_upload_log_with_connection)
            worker.log.connect(log_wrapper.emit)
            
            # 使用自定义信号包装器处理完成信号
            finished_wrapper = FinishedSignalWrapper(self, connection_name, self.on_upload_finished)
            worker.finished.connect(finished_wrapper.emit)
            
            # 线程清理
            worker.finished.connect(thread.quit)
            worker.finished.connect(worker.deleteLater)
            thread.finished.connect(thread.deleteLater)
            
            # 添加到线程跟踪器
            from main import ThreadTracker
            ThreadTracker.add_thread(thread, worker)
            
            self.upload_threads.append((thread, worker, connection_name))
            
            # 启动线程
            thread.start()
            
            # 在总体日志中记录线程启动
            self.upload_log.append(f"已启动上传线程: {connection_name}")
            
            # 处理事件队列，确保UI更新
            QCoreApplication.processEvents()

    def start_download(self):
        """开始下载（多线程并发执行）"""
        # 获取选中的连接
        selected_connections = self.get_checked_connections()
        if not selected_connections:
            self.show_message_box("警告", "请先选择一个SSH连接")
            return
            
        # 获取下载目录
        download_dir = self.download_dir_edit.text()
        if not download_dir:
            self.show_message_box("警告", "请先选择下载目录")
            return
            
        # 从下载文件列表中获取待下载的文件/目录列表
        remote_items = []
        for i in range(self.download_file_list.topLevelItemCount()):
            item = self.download_file_list.topLevelItem(i)
            text = item.text(0)
            if text.strip() and not text.startswith("下载目录:"):
                remote_items.append(text)
        
        if not remote_items:
            self.show_message_box("警告", "没有要下载的文件或目录")
            return
            
        # 清空之前的连接日志标签页（保留总体日志）
        for i in range(self.download_log_tabs.count() - 1, 0, -1):
            self.download_log_tabs.removeTab(i)
            
        # 初始化线程管理
        self.download_threads = []
        self.download_log_editors = {}  # 存储每个连接的日志编辑器
        self.completed_downloads = 0  # 跟踪完成的下载数量
        
        # 禁用下载按钮并显示进度条
        self.download_btn.setEnabled(False)
        self.download_progress.setVisible(True)
        self.download_progress.setValue(0)
        self.download_progress.setMaximum(len(selected_connections) * 100)  # 设置最大值
        
        # 记录开始下载的总体信息
        self.download_log.append(f"开始从 {len(selected_connections)} 台服务器下载文件...")
        self.download_log.append(f"下载目录: {download_dir}")
        self.download_log.append(f"文件数量: {len(remote_items)}")
        self.download_log.append("-" * 50)
        
        # 为每个连接创建下载线程和日志标签页
        for i, connection in enumerate(selected_connections):
            # 为每个连接创建日志标签页
            log_editor = QTextEdit()
            log_editor.setReadOnly(True)
            connection_name = f"{connection['name']} ({connection['host']})"
            self.download_log_tabs.addTab(log_editor, connection_name)
            self.download_log_editors[connection_name] = log_editor
            
            # 创建连接的深拷贝，避免引用问题
            import copy
            connection_copy = copy.deepcopy(connection)
            connection_copy['thread_index'] = i  # 添加线程索引，确保每个连接有唯一标识
            connection_copy['connection_id'] = f"download_{i}_{int(time.time() * 1000)}"  # 添加唯一ID
            
            # 创建下载工作器
            download_worker = DownloadWorker(
                files=remote_items[:],  # 创建列表副本
                directory=download_dir,
                connection=connection_copy,
                create_connection_dir=True,  # 为每个连接创建独立目录
                thread_index=i
            )
            
            # 创建线程
            thread = QThread()
            download_worker.moveToThread(thread)
            
            # 连接信号 - 使用字符串标识符替代闭包，确保Nuitka兼容性
            thread.started.connect(download_worker.run)
            
            # 使用自定义信号包装器，确保每个连接的信号正确路由
            progress_wrapper = ProgressSignalWrapper(self, i, self.update_download_progress_for_connection)
            download_worker.progress.connect(progress_wrapper.emit)
            
            # 使用自定义信号包装器处理日志
            log_wrapper = LogSignalWrapper(self, log_editor, connection_name, 
                                          self._append_download_log, self._append_download_log_with_connection)
            download_worker.log.connect(log_wrapper.emit)
            
            # 使用自定义信号包装器处理完成信号
            finished_wrapper = FinishedSignalWrapper(self, connection_name, self.on_download_finished)
            download_worker.finished.connect(finished_wrapper.emit)
            
            # 线程清理
            download_worker.finished.connect(thread.quit)
            download_worker.finished.connect(download_worker.deleteLater)
            thread.finished.connect(thread.deleteLater)
            
            # 添加到线程跟踪器
            from main import ThreadTracker
            ThreadTracker.add_thread(thread, download_worker)
            
            self.download_threads.append((thread, download_worker, connection_name))
            
            # 启动线程
            thread.start()
            
            # 在总体日志中记录线程启动
            self.download_log.append(f"已启动下载线程: {connection_name}")
    
    def _append_download_log(self, message, log_editor):
        """向特定连接的日志编辑器添加日志"""
        log_editor.append(message)
        
    def _append_download_log_with_connection(self, message, connection_name):
        """向总体日志添加带连接标识的日志"""
        self.download_log.append(f"[{connection_name}] {message}")
    
    def update_upload_progress_for_connection(self, value, idx):
        """为特定连接更新上传进度"""
        # 每个连接占用100个进度单位，根据连接索引计算基础进度
        base_progress = idx * 100
        current_progress = base_progress + value
        
        # 更新进度条
        if hasattr(self, 'upload_progress'):
            self.upload_progress.setValue(current_progress)
            
    def update_download_progress_for_connection(self, value, idx):
        """为特定连接更新下载进度"""
        # 每个连接占用100个进度单位，根据连接索引计算基础进度
        base_progress = idx * 100
        current_progress = base_progress + value
        
        # 更新进度条
        if hasattr(self, 'download_progress'):
            self.download_progress.setValue(current_progress)

    def on_upload_finished(self, connection_name=None):
        """上传完成回调"""
        # 增加完成计数
        if hasattr(self, 'completed_uploads'):
            self.completed_uploads += 1
        else:
            self.completed_uploads = 1

            
        # 在总体日志中记录完成信息
        if connection_name:
            self.upload_log.append(f"上传完成: {connection_name}")
        
        # 检查是否所有上传都已完成
        total_connections = len(self.upload_threads) if hasattr(self, 'upload_threads') else 0
        all_uploads_finished = self.completed_uploads >= total_connections
        
        # 在总体日志中显示进度
        self.upload_log.append(f"上传进度: {self.completed_uploads}/{total_connections}")
        
        # 只在所有上传完成后才恢复UI和清理资源
        if all_uploads_finished:
            # 恢复UI元素
            if hasattr(self, 'upload_btn'):
                self.upload_btn.setEnabled(True)
            if hasattr(self, 'upload_progress'):
                self.upload_progress.setVisible(False)
                self.upload_progress.setValue(0)
            
            # 重置完成计数
            self.completed_uploads = 0
            
            # 清理线程资源
            self._cleanup_upload_threads()
            
            # 显示完成提示
            self.upload_log.append("=" * 50)
            self.upload_log.append("所有服务器上传完成！")
            self.show_message_box("完成", f"已完成向 {total_connections} 台服务器的文件上传")
    
    def on_download_finished(self, connection_name=None):
        """下载完成回调"""
        # 增加完成计数
        if hasattr(self, 'completed_downloads'):
            self.completed_downloads += 1
        else:
            self.completed_downloads = 1
            
        # 在总体日志中记录完成信息
        if connection_name:
            self.download_log.append(f"下载完成: {connection_name}")
        
        # 检查是否所有下载都已完成
        total_connections = len(self.download_threads) if hasattr(self, 'download_threads') else 0
        all_downloads_finished = self.completed_downloads >= total_connections
        
        # 在总体日志中显示进度
        self.download_log.append(f"下载进度: {self.completed_downloads}/{total_connections}")
        
        # 只在所有下载完成后才恢复UI和清理资源
        if all_downloads_finished:
            # 恢复UI元素
            if hasattr(self, 'download_btn'):
                self.download_btn.setEnabled(True)
            if hasattr(self, 'download_progress'):
                self.download_progress.setVisible(False)
                self.download_progress.setValue(0)
            
            # 重置完成计数
            self.completed_downloads = 0
            
            # 清理线程资源
            self._cleanup_download_threads()
            
            # 显示完成提示
            self.download_log.append("=" * 50)
            self.download_log.append("所有服务器下载完成！")
            self.show_message_box("完成", f"已完成从 {total_connections} 台服务器的文件下载")
    
    def _cleanup_upload_threads(self):
        """清理上传线程资源"""
        if hasattr(self, 'upload_threads'):
            for thread, worker, connection_name in self.upload_threads:
                try:
                    # 从线程跟踪器中移除
                    from main import ThreadTracker
                    ThreadTracker.remove_thread(thread)
                    
                    # 确保线程已完成
                    if thread and thread.isRunning():
                        thread.quit()
                        thread.wait(1500)  # 增加等待时间确保线程完成
                    
                    # 清理资源
                    if worker:
                        worker.deleteLater()
                    if thread:
                        thread.deleteLater()
                        
                except (RuntimeError, AttributeError) as e:
                    # 线程对象可能已被删除
                    print(f"清理线程时出错: {e}")
            
            # 清空线程列表
            self.upload_threads = []

    def _cleanup_download_threads(self):
        """清理下载线程资源"""
        if hasattr(self, 'download_threads'):
            for thread, worker, connection_name in self.download_threads:
                try:
                    # 从线程跟踪器中移除
                    from main import ThreadTracker
                    ThreadTracker.remove_thread(thread)
                    
                    # 确保线程已完成
                    if thread and thread.isRunning():
                        thread.quit()
                        thread.wait(1500)  # 增加等待时间确保线程完成
                    
                    # 清理资源
                    if worker:
                        worker.deleteLater()
                    if thread:
                        thread.deleteLater()
                        
                except (RuntimeError, AttributeError) as e:
                    # 线程对象可能已被删除
                    print(f"清理下载线程时出错: {e}")
            
            # 清空线程列表
            self.download_threads = []
            
        # 检查是否所有下载都已完成
        all_downloads_finished = not self.download_threads
            
        # 恢复UI元素
        if hasattr(self, 'download_btn'):
            self.download_btn.setEnabled(True)
        if hasattr(self, 'download_progress'):
            self.download_progress.setVisible(False)
            
        # 只在所有下载完成后显示一次提示
        if all_downloads_finished:
            self.show_message_box("完成", "下载完成")
        
        # 停止定时器
        if hasattr(self, 'timer') and self.timer:
            try:
                self.timer.stop()
                self.timer.deleteLater()
                self.timer = None
            except RuntimeError:
                pass
        
        # 强制垃圾回收以清理所有QObject
        import gc
        gc.collect()
        
        # 再次检查并清理所有QThread对象
        for obj in gc.get_objects():
            try:
                # 修复：添加异常处理，防止访问已删除的对象
                if isinstance(obj, QThread):
                    try:
                        if obj.isRunning():
                            obj.quit()
                            if not obj.wait(1500):  # 增加等待时间
                                obj.terminate()
                                obj.wait(800)
                            obj.deleteLater()
                    except RuntimeError:
                        # 对象已被删除
                        pass
            except (RuntimeError, AttributeError):
                pass
        
        # 再次进行垃圾回收
        gc.collect()
        
        # 移除了对未定义的event参数的引用
        gc.collect()
        
    def get_checked_connections(self):
        """获取选中的连接"""
        checked_connections = []
        
        # 遍历所有顶级项
        for i in range(self.ssh_tree.topLevelItemCount()):
            top_item = self.ssh_tree.topLevelItem(i)
            self._collect_checked_connections(top_item, checked_connections)
            
        return checked_connections
        
    def _collect_checked_connections(self, item, connections):
        """递归收集选中的连接"""
        if item.checkState(0) == Qt.CheckState.Checked:
            # 如果是连接项，添加到列表
            item_data = item.data(0, Qt.ItemDataRole.UserRole)
            if isinstance(item_data, dict) and item_data.get("type") == "connection":
                connection_data = item_data["data"]
                connections.append(connection_data)
        
        # 递归处理子项
        for i in range(item.childCount()):
            self._collect_checked_connections(item.child(i), connections)
            
    def update_upload_progress(self, value):
        """更新上传进度"""
        current = self.upload_progress.value() + value
        self.upload_progress.setValue(current)
        
    def update_download_progress(self, value):
        """更新下载进度"""
        current = self.download_progress.value() + value
        self.download_progress.setValue(current)
        
    def browse_local_directory(self):
        """浏览本地目录"""
        directory = QFileDialog.getExistingDirectory(self, "选择本地目录")
        if directory:
            self.local_dir_edit.setText(directory)
            
    def browse_remote_directory(self):
        """浏览远程目录"""
        # 获取选中的连接
        checked_connections = self.get_checked_connections()
        if not checked_connections:
            QMessageBox.warning(self, "警告", "请至少选择一个连接")
            return
            
        # 只能选择一个连接进行远程目录浏览
        if len(checked_connections) > 1:
            QMessageBox.warning(self, "警告", "只能选择一个连接进行远程目录浏览")
            return
            
        connection = checked_connections[0]
        
        try:
            # 创建SSH客户端
            ssh_client = SSHClient(
                host=connection["host"],
                port=connection["port"],
                username=connection["username"],
                password=connection["password"]
            )
            ssh_client.connect()
            
            # 创建远程目录对话框
            dialog = RemoteDirDialog(self, ssh_client)
            if dialog.exec_() == QDialog.DialogCode.Accepted:
                selected_dir = dialog.get_selected_dir()
                # 修复属性引用错误：将remote_dir_edit改为upload_dir_edit
                self.upload_dir_edit.setText(selected_dir)
                
            ssh_client.close()
        except Exception as e:
            QMessageBox.critical(self, "错误", f"连接失败: {str(e)}")


class DirectorySelectorDialog(QDialog):
    """目录选择对话框"""
    def __init__(self, ssh_client, current_dir, parent=None):
        super().__init__(parent)
        self.ssh_client = ssh_client
        self.current_dir = current_dir
        self.item_types = {}
        self.init_ui()
        self.load_directory_content()
        self.apply_styles()
        
    def init_ui(self):
        self.setWindowTitle("选择远程目录")
        self.setModal(True)
        self.resize(600, 500)
        
        # 删除 "?" 按钮
        self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
        
        # 设置窗口图标
        icon_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'pic.ico')
        if os.path.exists(icon_path):
            self.setWindowIcon(QIcon(icon_path))
        
        layout = QVBoxLayout()
        layout.setSpacing(10)
        layout.setContentsMargins(15, 15, 15, 15)
        
        # 导航栏
        nav_layout = QHBoxLayout()
        nav_layout.setSpacing(8)
        self.back_btn = QPushButton("上级目录")
        self.back_btn.clicked.connect(self.go_back)
        self.refresh_btn = QPushButton("刷新")
        self.refresh_btn.clicked.connect(self.load_directory_content)
        self.path_edit = QLineEdit()
        self.path_edit.setText(self.current_dir)
        self.path_edit.returnPressed.connect(self.change_directory)
        
        nav_layout.addWidget(self.back_btn)
        nav_layout.addWidget(self.refresh_btn)
        nav_layout.addWidget(self.path_edit)
        
        # 目录列表
        self.dir_list = QListWidget()
        self.dir_list.itemDoubleClicked.connect(self.on_item_double_clicked)
        self.dir_list.setMinimumHeight(300)
        
        # 按钮
        button_layout = QHBoxLayout()
        button_layout.setSpacing(10)
        button_layout.addStretch()  # 添加弹性空间
        self.ok_btn = QPushButton("确定")
        self.ok_btn.setDefault(True)  # 设置默认按钮
        self.cancel_btn = QPushButton("取消")
        
        self.ok_btn.clicked.connect(self.accept)
        self.cancel_btn.clicked.connect(self.reject)
        
        button_layout.addWidget(self.ok_btn)
        button_layout.addWidget(self.cancel_btn)
        
        layout.addLayout(nav_layout)
        layout.addWidget(self.dir_list)
        layout.addLayout(button_layout)
        
        self.setLayout(layout)
        
    def load_directory_content(self):
        """加载目录内容"""
        try:
            self.dir_list.clear()
            self.path_edit.setText(self.current_dir)
            self.item_types.clear()
            
            # 获取目录内容
            items = self.ssh_client.sftp.listdir_attr(self.current_dir)
            
            # 添加上级目录项
            if self.current_dir != "/":
                parent_item = QListWidgetItem("..")
                parent_item.setData(Qt.ItemDataRole.UserRole, "..")
                # 设置目录图标
                parent_item.setIcon(self.style().standardIcon(self.style().StandardPixmap.SP_FileDialogToParent))
                self.dir_list.addItem(parent_item)
                self.item_types[".."] = "dir"
            
            # 添加目录和文件项
            for item in items:
                list_item = QListWidgetItem(item.filename)
                list_item.setData(Qt.ItemDataRole.UserRole, item.filename)
                
                # 检查是否为目录
                if item.st_mode & 0o040000:  # S_IFDIR - 目录
                    # 设置目录图标
                    list_item.setIcon(self.style().standardIcon(self.style().StandardPixmap.SP_DirIcon))
                    self.item_types[item.filename] = "dir"
                else:  # 文件
                    # 设置文件图标
                    list_item.setIcon(self.style().standardIcon(self.style().StandardPixmap.SP_FileIcon))
                    self.item_types[item.filename] = "file"
                    
                self.dir_list.addItem(list_item)
        except Exception as e:
            QMessageBox.critical(self, "错误", f"读取目录失败: {str(e)}")
            
    def go_back(self):
        """返回上级目录"""
        if self.current_dir != "/":
            parent_dir = os.path.dirname(self.current_dir.rstrip("/"))
            if not parent_dir:
                parent_dir = "/"
            self.current_dir = parent_dir
            self.load_directory_content()
            
    def change_directory(self):
        """跳转到指定目录"""
        new_dir = self.path_edit.text()
        if new_dir:
            try:
                # 验证目录是否存在
                self.ssh_client.sftp.stat(new_dir)
                self.current_dir = new_dir
                self.load_directory_content()
            except:
                QMessageBox.warning(self, "警告", "目录不存在")
                
    def on_item_double_clicked(self, item):
        """双击目录项"""
        dir_name = item.data(Qt.ItemDataRole.UserRole)
        if dir_name == "..":
            self.go_back()
        else:
            # 检查是否为目录
            item_type = self.item_types.get(dir_name, "unknown")
            if item_type == "dir":
                if self.current_dir == "/":
                    new_path = f"/{dir_name}"
                else:
                    new_path = f"{self.current_dir}/{dir_name}"
                self.current_dir = new_path
                self.load_directory_content()
            # 如果是文件则不处理双击事件
            
    def accept(self):
        """重写accept方法，获取选中的项目"""
        selected_items = self.dir_list.selectedItems()
        self.selected_items = []
        
        for item in selected_items:
            item_name = item.data(Qt.ItemDataRole.UserRole)
            if item_name != "..":  # 排除上级目录项
                # 构造完整路径（使用Linux路径分隔符处理远程路径）
                if self.current_dir == "/":
                    full_path = f"/{item_name}"
                else:
                    full_path = f"{self.current_dir}/{item_name}".replace("\\", "/")
                self.selected_items.append(full_path)
        
        super().accept()
            
    def get_selected_items(self):
        # 确保返回的路径使用Linux路径分隔符
        return [item.replace("\\", "/") for item in self.selected_items]


class ConnectionDialog(QDialog):
    def __init__(self, parent=None, connection_data=None):
        super().__init__(parent)
        self.connection_data = connection_data
        self.parent_window = parent  # 保存父窗口引用
        self.init_ui()
        # 移除对 apply_styles() 的调用，因为该方法未定义
        
    def init_ui(self):
        self.setWindowTitle("SSH连接设置")
        self.setModal(True)
        self.resize(400, 350)
        
        # 删除 "?" 按钮
        self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
        
        # 设置窗口图标
        icon_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'pic.ico')
        if os.path.exists(icon_path):
            self.setWindowIcon(QIcon(icon_path))
        
        layout = QFormLayout()
        layout.setLabelAlignment(Qt.AlignRight)
        layout.setHorizontalSpacing(15)
        layout.setVerticalSpacing(15)
        layout.setContentsMargins(20, 20, 20, 20)
        
        self.name_edit = QLineEdit()
        self.host_edit = QLineEdit()
        self.port_edit = QLineEdit()
        self.port_edit.setText("22")
        self.username_edit = QLineEdit()
        self.password_edit = QLineEdit()
        self.password_edit.setEchoMode(QLineEdit.Password)
        
        # 添加分组选择下拉框
        self.group_combo = QComboBox()
        self.load_groups()
        
        layout.addRow("连接名称:", self.name_edit)
        layout.addRow("主机地址:", self.host_edit)
        layout.addRow("端口:", self.port_edit)
        layout.addRow("用户名:", self.username_edit)
        layout.addRow("密码:", self.password_edit)
        layout.addRow("分组:", self.group_combo)
        
        buttons = QHBoxLayout()
        buttons.setSpacing(15)
        buttons.addStretch()
        ok_btn = QPushButton("确定")
        ok_btn.setDefault(True)
        cancel_btn = QPushButton("取消")
        
        ok_btn.clicked.connect(self.accept)
        cancel_btn.clicked.connect(self.reject)
        
        buttons.addWidget(ok_btn)
        buttons.addWidget(cancel_btn)
        
        layout.addRow(buttons)
        
        self.setLayout(layout)
        
        # 如果是编辑模式，填充数据
        if self.connection_data:
            self.name_edit.setText(self.connection_data.get("name", ""))
            self.host_edit.setText(self.connection_data.get("host", ""))
            self.port_edit.setText(str(self.connection_data.get("port", 22)))
            self.username_edit.setText(self.connection_data.get("username", ""))
            self.password_edit.setText(self.connection_data.get("password", ""))
            # 设置分组选择
            self.set_selected_group(self.connection_data.get("group_id"))
            
    def load_groups(self):
        """加载分组列表"""
        self.group_combo.clear()
        self.group_combo.addItem("请选择分组", None)
        
        if self.parent_window and hasattr(self.parent_window, 'settings'):
            groups = self.parent_window.settings.get_groups()
            for group in groups:
                self.group_combo.addItem(group["name"], group["id"])
                
    def set_selected_group(self, group_id):
        """设置选中的分组"""
        if group_id is not None:
            for i in range(self.group_combo.count()):
                if self.group_combo.itemData(i) == group_id:
                    self.group_combo.setCurrentIndex(i)
                    break
                    
    def get_data(self):
        # 获取选中的分组ID
        group_id = self.group_combo.itemData(self.group_combo.currentIndex())
        if group_id is None:
            group_id = self.group_combo.itemData(0) if self.group_combo.count() > 0 else None
            
        return {
            "name": self.name_edit.text(),
            "host": self.host_edit.text(),
            "port": self.port_edit.text(),
            "username": self.username_edit.text(),
            "password": self.password_edit.text(),
            "group_id": group_id
        }
        
    def accept(self):
        """重写accept方法，验证输入"""
        name = self.name_edit.text().strip()
        if not name:
            QMessageBox.warning(self, "警告", "连接名称不能为空")
            return
            
        # 检查是否选择了分组
        group_id = self.group_combo.itemData(self.group_combo.currentIndex())
        if group_id is None:
            QMessageBox.warning(self, "警告", "请选择一个分组")
            return
            
        super().accept()


class RemoteDirDialog(QDialog):
    def __init__(self, parent=None, ssh_client=None, current_dir=None):
        super().__init__(parent)
        self.ssh_client = ssh_client
        self.current_dir = current_dir or "/"
        self.selected_dir = None
        self.item_types = {}  # 存储每个项目的类型（目录或文件）
        self.init_ui()
        self.load_directory_content()
        
    def init_ui(self):
        self.setWindowTitle("选择远程文件或目录")
        self.setModal(True)
        self.resize(600, 500)
        
        # 删除 "?" 按钮

        self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
        
        # 设置窗口图标
        icon_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'pic.ico')
        if os.path.exists(icon_path):
            self.setWindowIcon(QIcon(icon_path))
        
        layout = QVBoxLayout()
        layout.setSpacing(10)
        layout.setContentsMargins(15, 15, 15, 15)
        
        # 导航栏
        nav_layout = QHBoxLayout()
        nav_layout.setSpacing(8)
        self.back_btn = QPushButton("上级目录")
        self.back_btn.clicked.connect(self.go_back)
        self.refresh_btn = QPushButton("刷新")
        self.refresh_btn.clicked.connect(self.load_directory_content)
        self.path_edit = QLineEdit()
        self.path_edit.setText(self.current_dir)
        self.path_edit.returnPressed.connect(self.change_directory)
        
        nav_layout.addWidget(self.back_btn)
        nav_layout.addWidget(self.refresh_btn)
        nav_layout.addWidget(self.path_edit)
        
        # 目录列表
        self.dir_list = QListWidget()
        self.dir_list.setSelectionMode(QListWidget.ExtendedSelection)  # 允许多选
        self.dir_list.itemDoubleClicked.connect(self.on_item_double_clicked)
        self.dir_list.setMinimumHeight(300)
        
        # 按钮
        button_layout = QHBoxLayout()
        button_layout.setSpacing(10)
        button_layout.addStretch()  # 添加弹性空间
        self.ok_btn = QPushButton("确定")
        self.ok_btn.setDefault(True)  # 设置默认按钮
        self.cancel_btn = QPushButton("取消")
        
        self.ok_btn.clicked.connect(self.accept)
        self.cancel_btn.clicked.connect(self.reject)
        
        button_layout.addWidget(self.ok_btn)
        button_layout.addWidget(self.cancel_btn)
        
        layout.addLayout(nav_layout)
        layout.addWidget(self.dir_list)
        layout.addLayout(button_layout)
        
        self.setLayout(layout)

    def load_directory_content(self):
        """加载目录内容"""
        try:
            self.dir_list.clear()
            self.path_edit.setText(self.current_dir)
            self.item_types.clear()
            
            # 获取目录内容
            items = self.ssh_client.sftp.listdir_attr(self.current_dir)
            
            # 添加上级目录项
            if self.current_dir != "/":
                parent_item = QListWidgetItem("..")
                parent_item.setData(Qt.ItemDataRole.UserRole, "..")
                # 设置目录图标
                parent_item.setIcon(self.style().standardIcon(self.style().StandardPixmap.SP_FileDialogToParent))
                self.dir_list.addItem(parent_item)
                self.item_types[".."] = "dir"
            
            # 添加目录和文件项
            for item in items:
                list_item = QListWidgetItem(item.filename)
                list_item.setData(Qt.ItemDataRole.UserRole, item.filename)
                
                # 检查是否为目录
                if item.st_mode & 0o040000:  # S_IFDIR - 目录
                    # 设置目录图标
                    list_item.setIcon(self.style().standardIcon(self.style().StandardPixmap.SP_DirIcon))
                    self.item_types[item.filename] = "dir"
                else:  # 文件
                    # 设置文件图标
                    list_item.setIcon(self.style().standardIcon(self.style().StandardPixmap.SP_FileIcon))
                    self.item_types[item.filename] = "file"
                    
                self.dir_list.addItem(list_item)
        except Exception as e:
            QMessageBox.critical(self, "错误", f"读取目录失败: {str(e)}")
            
    def go_back(self):
        """返回上级目录"""
        if self.current_dir != "/":
            parent_dir = os.path.dirname(self.current_dir.rstrip("/"))
            if not parent_dir:
                parent_dir = "/"
            self.current_dir = parent_dir
            self.load_directory_content()
            
    def change_directory(self):
        """跳转到指定目录"""
        new_dir = self.path_edit.text()
        if new_dir:
            try:
                # 验证目录是否存在
                self.ssh_client.sftp.stat(new_dir)
                self.current_dir = new_dir
                self.load_directory_content()
            except:
                QMessageBox.warning(self, "警告", "目录不存在")
                
    def on_item_double_clicked(self, item):
        """双击目录项"""
        dir_name = item.data(Qt.ItemDataRole.UserRole)
        if dir_name == "..":
            self.go_back()
        else:
            # 检查是否为目录
            item_type = self.item_types.get(dir_name, "unknown")
            if item_type == "dir":
                if self.current_dir == "/":
                    new_path = f"/{dir_name}"
                else:
                    new_path = f"{self.current_dir}/{dir_name}"
                self.current_dir = new_path
                self.load_directory_content()
            # 如果是文件则不处理双击事件
            
    def accept(self):
        """重写accept方法，获取选中的项目"""
        selected_items = self.dir_list.selectedItems()
        self.selected_items = []
        
        for item in selected_items:
            item_name = item.data(Qt.ItemDataRole.UserRole)
            if item_name != "..":  # 排除上级目录项
                # 构造完整路径（使用Linux路径分隔符处理远程路径）
                if self.current_dir == "/":
                    full_path = f"/{item_name}"
                else:
                    full_path = f"{self.current_dir}/{item_name}".replace("\\", "/")
                self.selected_items.append(full_path)
        
        super().accept()
            
    def get_selected_items(self):
        # 确保返回的路径使用Linux路径分隔符
        return [item.replace("\\", "/") for item in self.selected_items]




    def load_directory_content(self):
        """加载目录内容"""
        try:
            self.dir_list.clear()
            self.path_edit.setText(self.current_dir)
            self.item_types.clear()
            
            # 获取目录内容
            items = self.ssh_client.sftp.listdir_attr(self.current_dir)
            
            # 添加上级目录项
            if self.current_dir != "/":
                parent_item = QListWidgetItem("..")
                parent_item.setData(Qt.ItemDataRole.UserRole, "..")
                # 设置目录图标
                parent_item.setIcon(self.style().standardIcon(self.style().StandardPixmap.SP_FileDialogToParent))
                self.dir_list.addItem(parent_item)
                self.item_types[".."] = "dir"
            
            # 添加目录项
            for item in items:
                list_item = QListWidgetItem(item.filename)
                list_item.setData(Qt.ItemDataRole.UserRole, item.filename)
                
                # 检查是否为目录
                if item.st_mode & 0o040000:  # S_IFDIR - 目录
                    # 设置目录图标
                    list_item.setIcon(self.style().standardIcon(self.style().StandardPixmap.SP_DirIcon))
                    self.item_types[item.filename] = "dir"
                    
                self.dir_list.addItem(list_item)
        except Exception as e:
            QMessageBox.critical(self, "错误", f"读取目录失败: {str(e)}")
            
    def go_back(self):
        """返回上级目录"""
        if self.current_dir != "/":
            parent_dir = os.path.dirname(self.current_dir.rstrip("/"))
            if not parent_dir:
                parent_dir = "/"
            self.current_dir = parent_dir
            self.load_directory_content()
            
    def change_directory(self):
        """跳转到指定目录"""
        new_dir = self.path_edit.text()
        if new_dir:
            try:
                # 验证目录是否存在
                self.ssh_client.sftp.stat(new_dir)
                self.current_dir = new_dir
                self.load_directory_content()
            except:
                QMessageBox.warning(self, "警告", "目录不存在")
                
    def on_item_double_clicked(self, item):
        """双击目录项"""
        dir_name = item.data(Qt.ItemDataRole.UserRole)
        if dir_name == "..":
            self.go_back()
        else:
            # 检查是否为目录
            item_type = self.item_types.get(dir_name, "unknown")
            if item_type == "dir":
                if self.current_dir == "/":
                    new_path = f"/{dir_name}"
                else:
                    new_path = f"{self.current_dir}/{dir_name}"
                self.current_dir = new_path
                self.load_directory_content()
            # 如果是文件则不处理双击事件
            
    def accept(self):
        """重写accept方法，获取选中的目录"""
        selected_items = self.dir_list.selectedItems()
        if selected_items:
            item_name = selected_items[0].data(Qt.ItemDataRole.UserRole)
            if item_name != "..":  # 排除上级目录项
                # 构造完整路径（使用Linux路径分隔符处理远程路径）
                if self.current_dir == "/":
                    full_path = f"/{item_name}"
                else:
                    full_path = f"{self.current_dir}/{item_name}".replace("\\", "/")
                self.selected_dir = full_path
        
        super().accept()
            
    def get_selected_dir(self):
        return self.selected_dir



        

        
    def load_config(self):
        """加载上传配置"""
        # 从配置文件加载默认上传目录和文件列表
        settings = self.parent_window.settings if self.parent_window else None
        if settings:
            self.upload_dir_edit.setText(settings.get_default_upload_dir())
            # 加载默认上传文件列表
            default_files = settings.get_default_upload_files()
            self.upload_files_list.clear()
            for file in default_files:
                self.upload_files_list.addItem(file)
        else:
            # 使用默认值
            self.upload_dir_edit.setText("/tmp")
            
    def save_config(self):
        """保存上传配置"""
        # 将配置保存到配置文件
        settings = self.parent_window.settings if self.parent_window else None
        if settings:
            # 保存默认上传目录
            settings.set_default_upload_dir(self.upload_dir_edit.text())
            
            # 保存默认上传文件列表
            files = []
            for i in range(self.upload_files_list.count()):
                files.append(self.upload_files_list.item(i).text())
            settings.set_default_upload_files(files)
            
        QMessageBox.information(self, "提示", "配置已保存")
        self.accept()
        
    def browse_upload_directory(self):
        """浏览上传目录"""
        directory = QFileDialog.getExistingDirectory(self, "选择上传目录")
        if directory:
            self.upload_dir_edit.setText(directory)
            
    def add_upload_files(self):
        """添加上传文件"""
        files, _ = QFileDialog.getOpenFileNames(self, "选择文件", "", "All Files (*)")
        if files:
            for file in files:
                # 检查是否已存在
                exists = False
                for i in range(self.upload_files_list.count()):
                    if self.upload_files_list.item(i).text() == file:
                        exists = True
                        break
                if not exists:
                    self.upload_files_list.addItem(file)
                    
    def add_upload_dirs(self):
        """添加上传目录"""
        directory = QFileDialog.getExistingDirectory(self, "选择目录")
        if directory:
            # 检查是否已存在
            exists = False
            for i in range(self.upload_files_list.count()):
                if self.upload_files_list.item(i).text() == directory:
                    exists = True
                    break
            if not exists:
                self.upload_files_list.addItem(directory)
                
    def clear_download_files(self):
        """清空下载文件列表"""
        self.download_files_list.clear()
        
    def add_download_files(self):
        """添加下载文件路径"""
        # 允许用户手动输入文件路径
        dialog = QInputDialog(self)
        dialog.setWindowTitle("添加文件路径")
        dialog.setLabelText("请输入远程文件路径:")
        dialog.setOkButtonText("确定")
        dialog.setCancelButtonText("取消")
        
        # 删除 "?" 按钮
        dialog.setWindowFlags(dialog.windowFlags() & ~Qt.WindowContextHelpButtonHint)
        
        if dialog.exec_() == QDialog.Accepted:
            text = dialog.textValue()
            if text:
                # 检查是否已存在
                exists = False
                for i in range(self.download_files_list.count()):
                    if self.download_files_list.item(i).text() == text:
                        exists = True
                        break
                if not exists:
                    self.download_files_list.addItem(text)

    def clear_upload_files(self):
        """清空上传文件列表"""
        self.upload_files_list.clear()

    # 删除错误的load_config和save_config方法实现
    # 下面这些代码是错误的，应该删除：
    # def load_config(self):
    #     """加载配置"""
    #     config = self.parent_window.config
    #     self.upload_dir_edit.setText(config.get("upload_dir", ""))
    #     self.upload_files_list.clear()
    #     for file in config.get("upload_files", []):
    #         self.upload_files_list.addItem(file)
    #         
    # def save_config(self):
    #     """保存配置"""
    #     config = self.parent_window.config
    #     config["upload_dir"] = self.upload_dir_edit.text()
    #     config["upload_files"] = [self.upload_files_list.item(i).text() for i in range(self.upload_files_list.count())]
    #     self.parent_window.save_config()
    #     self.accept()
    
    def select_upload_dir(self):
        """选择上传目录"""
        directory = QFileDialog.getExistingDirectory(self, "选择目录")
        if directory:
            self.upload_dir_edit.setText(directory)
            
    def add_upload_files(self):
        """添加上传文件"""
        files, _ = QFileDialog.getOpenFileNames(self, "选择文件")
        if files:
            for file in files:
                # 检查是否已存在
                exists = False
                for i in range(self.upload_files_list.count()):
                    if self.upload_files_list.item(i).text() == file:
                        exists = True
                        break
                if not exists:
                    self.upload_files_list.addItem(file)
                    
    def add_upload_dirs(self):
        """添加上传目录"""
        directory = QFileDialog.getExistingDirectory(self, "选择目录")
        if directory:
            # 检查是否已存在
            exists = False
            for i in range(self.upload_files_list.count()):
                if self.upload_files_list.item(i).text() == directory:
                    exists = True
                    break
            if not exists:
                self.upload_files_list.addItem(directory)
                
    def remove_selected_upload_files(self):
        """移除选中的上传文件"""
        for item in self.upload_files_list.selectedItems():
            self.upload_files_list.takeItem(self.upload_files_list.row(item))
            
    def clear_upload_files(self):
        """清空上传文件列表"""
        self.upload_files_list.clear()

class DownloadConfigDialog(QDialog):
    """下载配置对话框"""
    def __init__(self, parent=None):
        super().__init__(parent)
        self.parent_window = parent
        self.init_ui()
        self.load_config()
        # 移除对 apply_styles() 的调用，因为该方法未定义
        
    def init_ui(self):
        self.setWindowTitle("文件下载配置")
        self.setModal(True)
        self.resize(500, 400)
        
        # 删除 "?" 按钮
        self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
        
        # 设置窗口图标
        icon_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'pic.ico')
        if os.path.exists(icon_path):
            self.setWindowIcon(QIcon(icon_path))
        
        layout = QVBoxLayout()
        layout.setSpacing(15)
        layout.setContentsMargins(20, 20, 20, 20)
        
        # 默认下载目录
        download_dir_layout = QHBoxLayout()
        download_dir_layout.setSpacing(8)
        download_dir_label = QLabel("默认下载目录:")
        self.download_dir_edit = QLineEdit()
        self.browse_download_dir_btn = QPushButton("浏览")
        self.browse_download_dir_btn.clicked.connect(self.browse_download_directory)
        
        download_dir_layout.addWidget(download_dir_label)
        download_dir_layout.addWidget(self.download_dir_edit)
        download_dir_layout.addWidget(self.browse_download_dir_btn)
        
        # 默认待下载文件列表
        download_files_label = QLabel("默认待下载文件列表:")
        self.download_files_list = QListWidget()
        self.download_files_list.setSelectionMode(QListWidget.ExtendedSelection)
        
        # 文件操作按钮
        file_buttons_layout = QHBoxLayout()
        self.add_download_file_btn = QPushButton("添加文件路径")
        self.remove_download_file_btn = QPushButton("移除选中")
        self.clear_download_files_btn = QPushButton("清空列表")
        
        self.add_download_file_btn.clicked.connect(self.add_download_files)
        self.remove_download_file_btn.clicked.connect(self.remove_selected_download_files)
        self.clear_download_files_btn.clicked.connect(self.clear_download_files)
        
        file_buttons_layout.addWidget(self.add_download_file_btn)
        file_buttons_layout.addWidget(self.remove_download_file_btn)
        file_buttons_layout.addWidget(self.clear_download_files_btn)
        
        # 按钮布局
        button_layout = QHBoxLayout()
        button_layout.addStretch()
        self.save_btn = QPushButton("保存")
        self.cancel_btn = QPushButton("取消")
        
        self.save_btn.clicked.connect(self.save_config)
        self.cancel_btn.clicked.connect(self.reject)
        
        button_layout.addWidget(self.save_btn)
        button_layout.addWidget(self.cancel_btn)
        
        layout.addLayout(download_dir_layout)
        layout.addWidget(download_files_label)
        layout.addWidget(self.download_files_list)
        layout.addLayout(file_buttons_layout)
        layout.addLayout(button_layout)
        
        self.setLayout(layout)
        
    def load_config(self):
        """加载下载配置"""
        # 从配置文件加载默认下载目录和文件列表
        settings = self.parent_window.settings if self.parent_window else None
        if settings:
            self.download_dir_edit.setText(settings.get_default_download_dir())
            # 加载默认下载文件列表
            default_files = settings.get_default_download_files()
            self.download_files_list.clear()
            for file in default_files:
                self.download_files_list.addItem(file)
        else:
            # 使用默认值
            self.download_dir_edit.setText("download")
        
    def save_config(self):
        """保存下载配置"""
        # 将配置保存到配置文件
        settings = self.parent_window.settings if self.parent_window else None
        if settings:
            # 保存默认下载目录
            settings.set_default_download_dir(self.download_dir_edit.text())
            
            # 保存默认下载文件列表
            files = []
            for i in range(self.download_files_list.count()):
                files.append(self.download_files_list.item(i).text())
            settings.set_default_download_files(files)
            
        QMessageBox.information(self, "提示", "配置已保存")
        self.accept()
        
    def browse_download_directory(self):
        """浏览下载目录"""
        directory = QFileDialog.getExistingDirectory(self, "选择下载目录")
        if directory:
            self.download_dir_edit.setText(directory)
            
    def remove_selected_download_files(self):
        """移除选中的下载文件"""
        for item in self.download_files_list.selectedItems():
            self.download_files_list.takeItem(self.download_files_list.row(item))
            
    def clear_download_files(self):
        """清空下载文件列表"""
        self.download_files_list.clear()
        
    def add_download_files(self):
        """添加下载文件路径"""
        # 允许用户手动输入文件路径
        dialog = QInputDialog(self)
        dialog.setWindowTitle("添加文件路径")
        dialog.setLabelText("请输入远程文件路径:")
        dialog.setOkButtonText("确定")
        dialog.setCancelButtonText("取消")
        
        # 删除 "?" 按钮
        dialog.setWindowFlags(dialog.windowFlags() & ~Qt.WindowContextHelpButtonHint)
        
        if dialog.exec_() == QDialog.Accepted:
            text = dialog.textValue()
            if text:
                # 检查是否已存在
                exists = False
                for i in range(self.download_files_list.count()):
                    if self.download_files_list.item(i).text() == text:
                        exists = True
                        break
                if not exists:
                    self.download_files_list.addItem(text)

class UploadConfigDialog(QDialog):
    """上传配置对话框"""
    def __init__(self, parent=None):
        super().__init__(parent)
        self.parent_window = parent
        self.init_ui()
        self.load_config()
        # 移除对 apply_styles() 的调用，因为该方法未定义
        
    def init_ui(self):
        self.setWindowTitle("文件上传配置")
        self.setModal(True)
        self.resize(500, 400)
        
        # 删除 "?" 按钮
        self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
        
        # 设置窗口图标
        icon_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'pic.ico')
        if os.path.exists(icon_path):
            self.setWindowIcon(QIcon(icon_path))
        
        layout = QVBoxLayout()
        layout.setSpacing(15)
        layout.setContentsMargins(20, 20, 20, 20)
        
        # 默认上传目录
        upload_dir_layout = QHBoxLayout()
        upload_dir_layout.setSpacing(8)
        upload_dir_label = QLabel("默认上传目录:")
        self.upload_dir_edit = QLineEdit()
        self.browse_upload_dir_btn = QPushButton("浏览")
        self.browse_upload_dir_btn.clicked.connect(self.browse_upload_directory)
        
        upload_dir_layout.addWidget(upload_dir_label)
        upload_dir_layout.addWidget(self.upload_dir_edit)
        upload_dir_layout.addWidget(self.browse_upload_dir_btn)
        
        # 默认待上传文件列表
        upload_files_label = QLabel("默认待上传文件列表:")
        self.upload_files_list = QListWidget()
        self.upload_files_list.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection)
        self.upload_files_list.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection)
        self.upload_files_list.setDragEnabled(True)
        self.upload_files_list.setAcceptDrops(True)
        self.upload_files_list.setDropIndicatorShown(True)
        self.upload_files_list.setDragDropMode(QAbstractItemView.InternalMove)
        self.upload_files_list.setAlternatingRowColors(True)
        self.upload_files_list.setSortingEnabled(True)
        # 移除对不存在方法 show_context_menu 的调用
        # self.upload_files_list.setContextMenuPolicy(Qt.CustomContextMenu)
        # self.upload_files_list.customContextMenuRequested.connect(self.show_context_menu)
        
        # 文件操作按钮
        file_buttons_layout = QHBoxLayout()
        self.add_upload_file_btn = QPushButton("添加文件")
        self.add_upload_dir_btn = QPushButton("添加目录")
        self.remove_upload_file_btn = QPushButton("移除选中")
        self.clear_upload_files_btn = QPushButton("清空列表")
        
        self.add_upload_file_btn.clicked.connect(self.add_upload_files)
        self.add_upload_dir_btn.clicked.connect(self.add_upload_dirs)
        self.remove_upload_file_btn.clicked.connect(self.remove_selected_upload_files)
        self.clear_upload_files_btn.clicked.connect(self.clear_upload_files)
        
        file_buttons_layout.addWidget(self.add_upload_file_btn)
        file_buttons_layout.addWidget(self.add_upload_dir_btn)
        file_buttons_layout.addWidget(self.remove_upload_file_btn)
        file_buttons_layout.addWidget(self.clear_upload_files_btn)
        
        # 按钮布局
        button_layout = QHBoxLayout()
        button_layout.addStretch()
        self.save_btn = QPushButton("保存")
        self.cancel_btn = QPushButton("取消")
        
        self.save_btn.clicked.connect(self.save_config)
        self.cancel_btn.clicked.connect(self.reject)
        
        button_layout.addWidget(self.save_btn)
        button_layout.addWidget(self.cancel_btn)
        
        layout.addLayout(upload_dir_layout)
        layout.addWidget(upload_files_label)
        layout.addWidget(self.upload_files_list)
        layout.addLayout(file_buttons_layout)
        layout.addLayout(button_layout)
        
        self.setLayout(layout)
        
    def load_config(self):
        """加载上传配置"""
        # 从配置文件加载默认上传目录和文件列表
        settings = self.parent_window.settings if self.parent_window else None
        if settings:
            self.upload_dir_edit.setText(settings.get_default_upload_dir())
            # 加载默认上传文件列表
            default_files = settings.get_default_upload_files()
            self.upload_files_list.clear()
            for file in default_files:
                self.upload_files_list.addItem(file)
        else:
            # 使用默认值
            self.upload_dir_edit.setText("/tmp")
            
    def save_config(self):
        """保存上传配置"""
        # 将配置保存到配置文件
        settings = self.parent_window.settings if self.parent_window else None
        if settings:
            # 保存默认上传目录
            settings.set_default_upload_dir(self.upload_dir_edit.text())
            
            # 保存默认上传文件列表
            files = []
            for i in range(self.upload_files_list.count()):
                files.append(self.upload_files_list.item(i).text())
            settings.set_default_upload_files(files)
            
        QMessageBox.information(self, "提示", "配置已保存")
        self.accept()
        
    def browse_upload_directory(self):
        """浏览上传目录"""
        directory = QFileDialog.getExistingDirectory(self, "选择上传目录")
        if directory:
            self.upload_dir_edit.setText(directory)
            
    def add_upload_files(self):
        """添加上传文件"""
        files, _ = QFileDialog.getOpenFileNames(self, "选择文件", "", "All Files (*)")
        if files:
            for file in files:
                # 检查是否已存在
                exists = False
                for i in range(self.upload_files_list.count()):
                    if self.upload_files_list.item(i).text() == file:
                        exists = True
                        break
                if not exists:
                    self.upload_files_list.addItem(file)
                    
    def add_upload_dirs(self):
        """添加上传目录"""
        directory = QFileDialog.getExistingDirectory(self, "选择目录")
        if directory:
            # 检查是否已存在
            exists = False
            for i in range(self.upload_files_list.count()):
                if self.upload_files_list.item(i).text() == directory:
                    exists = True
                    break
            if not exists:
                self.upload_files_list.addItem(directory)
                
    def remove_selected_upload_files(self):
        """移除选中的上传文件"""
        for item in self.upload_files_list.selectedItems():
            self.upload_files_list.takeItem(self.upload_files_list.row(item))
            
    def clear_upload_files(self):
        """清空上传文件列表"""
        self.upload_files_list.clear()


class DirectorySelectorDialog(QDialog):
    """目录选择对话框"""
    def __init__(self, ssh_client, current_dir, parent=None):
        super().__init__(parent)
        self.ssh_client = ssh_client
        self.current_dir = current_dir
        self.item_types = {}
        self.init_ui()
        self.load_directory_content()
        self.apply_styles()
        
    def apply_styles(self):
        """应用现代化样式表"""
        style = """
        QDialog {
            background-color: #f0f0f0;
        }
        
        QLineEdit {
            border: 1px solid #d0d0d0;
            border-radius: 6px;
            padding: 6px;
            background-color: white;
        }
        
        QLineEdit:focus {
            border: 2px solid #4a90e2;
        }
        
        QPushButton {
            background-color: #4a90e2;
            border: none;
            color: white;
            padding: 6px 12px;
            border-radius: 6px;
            font-size: 13px;
        }
        
        QPushButton:hover {
            background-color: #357ae8;
        }
        
        QPushButton:pressed {
            background-color: #2a64c4;
        }
        
        QPushButton:disabled {
            background-color: #cccccc;
            color: #666666;
        }
        
        QPushButton[text="上级目录"] {
            background-color: #888888;
        }
        
        QPushButton[text="上级目录"]:hover {
            background-color: #666666;
        }
        
        QPushButton[text="刷新"] {
            background-color: #50c878;
        }
        
        QPushButton[text="刷新"]:hover {
            background-color: #3eb489;
        }
        
        QPushButton[text="确定"] {
            background-color: #50c878;
            font-weight: bold;
        }
        
        QPushButton[text="确定"]:hover {
            background-color: #3eb489;
        }
        
        QPushButton[text="取消"] {
            background-color: #e74c3c;
        }
        
        QPushButton[text="取消"]:hover {
            background-color: #c0392b;
        }
        
        # QListWidget {
        #     border: 1px solid #d0d0d0;
        #     border-radius: 6px;
        #     background-color: white;
        # }
        
        QListWidget::item {
            /* Deleteindicator related QSS, restore native checkbox √ effect */
        }
        
        /* 为上传和下载文件列表添加边框样式，不影响SSH连接树 */
        QTreeWidget#upload_file_list, QTreeWidget#download_file_list {
            border: 1px solid #d0d0d0;
            border-radius: 6px;
            background-color: white;
        }
        
        QHeaderView::section {
            padding: 4px;
        }
        
        QListWidget::item:selected {
            background-color: #4a90e2;
            color: white;
        }
        """
        self.setStyleSheet(style)
        
    def init_ui(self):
        self.setWindowTitle("选择远程目录")
        self.setModal(True)
        self.resize(600, 500)
        
        # 设置窗口图标
        icon_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'pic.ico')
        if os.path.exists(icon_path):
            self.setWindowIcon(QIcon(icon_path))
        
        layout = QVBoxLayout()
        layout.setSpacing(10)
        layout.setContentsMargins(15, 15, 15, 15)
        
        # 导航栏
        nav_layout = QHBoxLayout()
        nav_layout.setSpacing(8)
        self.back_btn = QPushButton("上级目录")
        self.back_btn.clicked.connect(self.go_back)
        self.refresh_btn = QPushButton("刷新")
        self.refresh_btn.clicked.connect(self.load_directory_content)
        self.path_edit = QLineEdit()
        self.path_edit.setText(self.current_dir)
        self.path_edit.returnPressed.connect(self.change_directory)
        
        nav_layout.addWidget(self.back_btn)
        nav_layout.addWidget(self.refresh_btn)
        nav_layout.addWidget(self.path_edit)
        
        # 目录列表
        self.dir_list = QListWidget()
        self.dir_list.itemDoubleClicked.connect(self.on_item_double_clicked)
        self.dir_list.setMinimumHeight(300)
        
        # 按钮
        button_layout = QHBoxLayout()
        button_layout.setSpacing(10)
        button_layout.addStretch()  # 添加弹性空间
        self.ok_btn = QPushButton("确定")
        self.ok_btn.setDefault(True)  # 设置默认按钮
        self.cancel_btn = QPushButton("取消")
        
        self.ok_btn.clicked.connect(self.accept)
        self.cancel_btn.clicked.connect(self.reject)
        
        button_layout.addWidget(self.ok_btn)
        button_layout.addWidget(self.cancel_btn)
        
        layout.addLayout(nav_layout)
        layout.addWidget(self.dir_list)
        layout.addLayout(button_layout)
        
        self.setLayout(layout)
        
    def load_directory_content(self):
        """加载目录内容"""
        try:
            self.dir_list.clear()
            self.path_edit.setText(self.current_dir)
            self.item_types.clear()
            
            # 获取目录内容
            items = self.ssh_client.sftp.listdir_attr(self.current_dir)
            
            # 添加上级目录项
            if self.current_dir != "/":
                parent_item = QListWidgetItem("..")
                parent_item.setData(Qt.ItemDataRole.UserRole, "..")
                # 设置目录图标
                parent_item.setIcon(self.style().standardIcon(self.style().StandardPixmap.SP_FileDialogToParent))
                self.dir_list.addItem(parent_item)
                self.item_types[".."] = "dir"
            
            # 添加目录和文件项
            for item in items:
                list_item = QListWidgetItem(item.filename)
                list_item.setData(Qt.ItemDataRole.UserRole, item.filename)
                
                # 检查是否为目录
                if item.st_mode & 0o040000:  # S_IFDIR - 目录
                    # 设置目录图标
                    list_item.setIcon(self.style().standardIcon(self.style().StandardPixmap.SP_DirIcon))
                    self.item_types[item.filename] = "dir"
                else:  # 文件
                    # 设置文件图标
                    list_item.setIcon(self.style().standardIcon(self.style().StandardPixmap.SP_FileIcon))
                    self.item_types[item.filename] = "file"
                    
                self.dir_list.addItem(list_item)
        except Exception as e:
            QMessageBox.critical(self, "错误", f"读取目录失败: {str(e)}")
            
    def go_back(self):
        """返回上级目录"""
        if self.current_dir != "/":
            parent_dir = os.path.dirname(self.current_dir.rstrip("/"))
            if not parent_dir:
                parent_dir = "/"
            self.current_dir = parent_dir
            self.load_directory_content()
            
    def change_directory(self):
        """跳转到指定目录"""
        new_dir = self.path_edit.text()
        if new_dir:
            try:
                # 验证目录是否存在
                self.ssh_client.sftp.stat(new_dir)
                self.current_dir = new_dir
                self.load_directory_content()
            except:
                QMessageBox.warning(self, "警告", "目录不存在")
                
    def on_item_double_clicked(self, item):
        """双击目录项"""
        dir_name = item.data(Qt.ItemDataRole.UserRole)
        if dir_name == "..":
            self.go_back()
        else:
            # 检查是否为目录
            item_type = self.item_types.get(dir_name, "unknown")
            if item_type == "dir":
                if self.current_dir == "/":
                    new_path = f"/{dir_name}"
                else:
                    new_path = f"{self.current_dir}/{dir_name}"
                self.current_dir = new_path
                self.load_directory_content()
            # 如果是文件则不处理双击事件
            
    def accept(self):
        """重写accept方法，允许选择文件或目录"""
        # 允许选择文件或目录，不再限制只能选择目录
        super().accept()
            
    def get_selected_dir(self):
        """获取选中的文件或目录路径"""
        current_item = self.dir_list.currentItem()
        if current_item:
            item_name = current_item.data(Qt.ItemDataRole.UserRole)
            if item_name == "..":
                # 如果选择的是上级目录，返回当前目录
                return self.current_dir.replace("\\", "/")
            else:
                # 返回选中的文件或目录的完整路径
                if self.current_dir == "/":
                    selected_path = f"/{item_name}"
                else:
                    selected_path = f"{self.current_dir}/{item_name}"
                return selected_path.replace("\\", "/")
        # 如果没有选中任何项，返回当前目录
        return self.current_dir.replace("\\", "/")


class ConnectionTestWorker(QObject):
    """连接测试工作线程"""
    
    # 信号定义
    result = Signal(dict, bool, str)  # 连接信息, 是否成功, 消息
    finished = Signal()  # 完成信号
    
    def __init__(self, connection):
        super().__init__()
        self.connection = connection
        self._running = True
        
    def run(self):
        """执行连接测试"""
        ssh_client = None
        start_time = time.time()
        
        try:
            # 创建SSH客户端
            ssh_client = SSHClient(
                host=self.connection["host"],
                port=self.connection["port"],
                username=self.connection["username"],
                password=self.connection["password"]
            )
            
            # 尝试连接，设置超时时间为10秒
            ssh_client.connect(timeout=10)
            
            # 连接成功
            self.result.emit(self.connection, True, "连接成功")
        except Exception as e:
            # 连接失败
            error_msg = str(e)
            if not error_msg:
                error_msg = "未知错误"
            
            # 区分不同类型的错误，提供更明确的错误信息
            if isinstance(e, socket.timeout) or "连接超时" in error_msg:
                error_msg = f"连接超时: 无法在10秒内连接到{self.connection['host']}:{self.connection['port']}"
            elif "认证失败" in error_msg:
                error_msg = "认证失败: 用户名或密码错误"
            elif "DNS解析失败" in error_msg:
                error_msg = f"DNS解析失败: 无法解析主机名 {self.connection['host']}"
            
            self.result.emit(self.connection, False, error_msg)
        except:
            # 捕获所有其他异常，确保线程不会无声失败
            self.result.emit(self.connection, False, "连接测试过程中发生未知错误")
        finally:
            # 确保关闭连接
            if ssh_client:
                try:
                    ssh_client.close()
                except:
                    pass
                    
            # 记录完成时间，用于调试
            elapsed_time = time.time() - start_time
            # 发射完成信号
            self.finished.emit()
            
    def stop(self):
        """停止测试线程"""
        self._running = False